
エクセル2010です。
以下のコードで任意のフォルダ内のエクセルBOOKから所定のデータを取得できます。
しかし、指定フォルダ内に読み取りパスワードが設定されたものがあると、開くことができずに止まってしまいます。
読み取りパスワードが同一で、事前に分かっていればコードにPassword:="AAAABBBB" などと書き入れればいいと思うのですが、事前にはわかりませんし、パスワードもそれぞれ異なります。
そこで、開けなかった場合には、そのBOOKを飛ばしてすすみ、別シートに飛ばしたBOOK名を記録しておきたいのです。
(BOOK作成者にあとからパスワードを聞くため)
しかし、残念ながらどのように書けばいいのか思いつきません。
ご指導いただければ幸いです。
Sub TEST001()
Dim wb(1) As Workbook
Dim ws(1) As Worksheet
Dim myFdr As String, fn As String
Dim i As Long
With Application.FileDialog(msoFileDialogFolderPicker) 'フォルダ指定
If .Show = True Then
myFdr = .SelectedItems(1)
Else
Exit Sub
End If
End With
Application.ScreenUpdating = False '画面更新を一時停止
Set wb(0) = ThisWorkbook 'このコピー先ブックをwb(0)とする。
Set ws(0) = wb(0).Sheets(1) 'wb(0)の1枚目のシートをws(0)とする。
fn = Dir(myFdr & "\*.xls*") 'フォルダ内のExcelブックを検索
Do Until fn = Empty '全て検索
Application.EnableEvents = False
Set wb(1) = Workbooks.Open(myFdr & "\" & fn, UpdateLinks:=False, ReadOnly:=True) 'そのブックを開きwb(1)とする。
Set ws(1) = wb(1).Worksheets(1)
i = i + 1
ws(0).Cells(i, "A").Value = ws(1).Range("B2") 'ws(0)に転記
ws(0).Cells(i, "B").Value = wb(1).Name
ws(0).Cells(i, "C").Value = ws(1).Name
wb(1).Close (False) '保存せず閉じる
Application.EnableEvents = True
fn = Dir 'フォルダ内の次のExcelブックを検索
Loop '繰り返す
Application.ScreenUpdating = True '画面更新停止を解除
MsgBox i & "個取得"
End Sub
No.3ベストアンサー
- 回答日時:
#2、cjです。
#2お礼欄へのレスです。>> ThisWorkbook(の名前)を処理から除外しておいた方が安全ではあります。(コードは示しません)
>
>以下のようにしてみましたが、よろしいでしょうか?
はい。考え方として正しいですし、完全に解決出来ています。
拘るなら、ループの中で何度も取得し直す固定的なプロパティは、
事前に変数に纏めておいた方が何かと有利になりますね。
>> 同様にワークブックの二重起動についても考慮に入れた方がより無難になるかも、です。
>なるほど、すでに開いていることも考えられますね。
>残念ながら、この対応法がわかりません。
基本的なこととして、部分的に仕様を変える時には、視野を拡げて、
他の部分を含めて全体的な仕様への影響を考えに入れるよう習慣付けた方が好いです。
例えば、ThisWorkbook以外に、
転記元となるべきブックが実行前に既に開いていたとして、
そのブックに未保存データがある場合、どうしようか、とか。
仮に二重に開くことを回避できても、そのまま目を瞑って転記したとすると、
そのブックを上書き保存せずに閉じるようなことがあれば、
せっかく転記したリストに不整合が起きる可能性がある訳で、、、。
未保存の問題をクリア出来たとして、
その(開いていた)ブックを含めて、転記したブックを一様に閉じてしまったりしたら、
他の編集作業に支障があるのではないか、とか、、、。
まず大雑把な仕様の方向付けを仮に決めてみて、
その為に必要な技術で、足りないものがあれば、習得に努めて、
見通しが立ったら仮の仕様を再検証してみて、
大雑把に書いてみて、調整を加えて、ってな流れで考えてみたり、、、。
そんなこんなで、ユーザー目線を加味しながら妥協点を見つけてみて、仕上げていく、とか。
実務上の必要と十分に照らして仕様を整理することから始めないと、
"対応法が"わからないのは誰でも一緒です。
でも、なんか、今回の場合は、大変そうだから、
ThisWorkbookとPERSONAL.XLS以外のブックが開いていないことを確認
してから処理に進むようにしてみる、とか、
もう少し踏み込んで、、、
ThisWorkbookとPERSONAL.XLS以外に開いているブックが、
指定したフォルダにあるかどうかを先に確認して、
強制的に閉じちゃう、か、処理を中止して閉じてから実行して貰う、とか、
簡単に済ませちゃってもいいでしょうね。
近隣のQAを見ても、何も手当てしてない場合が多いようですし、、、。
ただ、今回はブックの開き方に特殊を認めている訳ですから、
二重に開くことを無視して実行するのだけは避けた方がいいでしょう。
最悪でも運用上の注意喚起(周知)は必要です。
参考に、前段に挙げた問題点に対して積極的に対策する方法を考えてみました。
次の投稿で書いたものを掲げてみます。
既出のコードでは、未保存の場合への対策が難しかったので、
手法的に大幅に変えたものになりました。
他にもケアしないといけないと気づいていることもあるのですが、
(大文字小文字を区別しないファイル名判定、とか、環境的な条件とか、色々)
今の処の(短時間で形にする為の)妥協点、ということです。
ただ、エラー処理の仕方は#2よりだいぶマシになっています。
Shellを扱うかどうかは別にしても、
処理対象の一覧を先に取得しておくのは、
事後の処理に何かと融通性をもたらすかとは思います。
あくまで参考程度ですが、、、。
(次の投稿に続きます)
No.4
- 回答日時:
(前の投稿の続きです)
Sub Re8470695j()
' ' ーーーーーーーーー
' ' フォルダ指定
Dim sDir As String ' 指定フォルダ名
With Application.FileDialog(msoFileDialogFolderPicker)
' ' ▲例:自ブックのフォルダの一階層上を表示
.InitialFileName = Left(ThisWorkbook.Path, InStrRev(ThisWorkbook.Path, "\") - 1) ' ▲仮の例です。変更/省略可。
' ' ファイル名一覧取得
If .Show = True Then
sDir = .SelectedItems(1)
Else
Exit Sub
End If
End With
' ' ーーーーーーーーー
' ' ファイル名一覧取得
'Dim oWSH
Dim oWSH As Object ' WScript.Shell As IWshRuntimeLibrary.WshShell (Windows Scripting Host Object Model)
Dim sCmd As String ' コマンドプロンプト
Dim sBuf As String ' 転記元ファイル名一覧(CrLf区切り)
Set oWSH = CreateObject("WScript.Shell")
' ' コマンドプロンプト:指定フォルダの"*xls*"ファイル名の一覧を取得
sCmd = "%ComSpec% /c dir " & sDir & "\*.xls/B"
With oWSH.Exec(sCmd) ' コマンド実行
Do While .Status = 0
DoEvents ' 非同期実行を待機
Loop
' ' 転記元ファイル名一覧(CrLf区切り)を読み込み(前後にCrLf在り)
sBuf = vbCrLf & .StdOut.ReadAll
End With
Set oWSH = Nothing
If sBuf = vbCrLf Then MsgBox "空っぽ、中止": Exit Sub
' ' ーーーーーーーーー
' ' 転記元ファイル名一覧から自ブックを除外
If ThisWorkbook.Path = sDir Then
sBuf = Replace(sBuf, vbCrLf & ThisWorkbook.Name, "")
End If
' ' ーーーーーーーーー
' ' 転記元の各ブックが実行前から開いていた場合
' ' 未保存なら上書きを強制|または処理中止
Dim oWbk As Workbook
For Each oWbk In Workbooks
' ' ーーーー実行前から開いていたブック名が転記元ファイル名一覧に含まれ、
' ' ーーーーそのブックが指定のフォルダに存在するならば
If InStr(sBuf, vbCrLf & oWbk.Name) Then
If oWbk.Path = sDir Then
If Not oWbk.Saved Then
If MsgBox("処理の続行には上書き保存する必要あり" & vbLf & vbTab & oWbk.Name & vbLf & "続行?", vbYesNo) = vbYes Then
oWbk.Save
Else
MsgBox "中止": Exit Sub
End If
End If
Else
MsgBox "転記元に指定したブックと同名ブックが開いているので中止": Exit Sub
End If
End If
Next
' ' ーーーーーーーーー
' ' ファイル名一覧の、前後のCrLfトル
sBuf = Mid$(sBuf, 3, Len(sBuf) - 4)
' ' ーーーーーーーーー
' ' ファイル名一覧から、転記元ブック名の配列
Dim arrFn() As String ' 転記元ブック名の配列
arrFn() = Split(sBuf, vbCrLf)
' ' ーーーーーーーーー
' ' 転記元ブック名の配列を総当りで、転記
Dim wsPrint As Worksheet ' 転記先シート
Dim wsLog As Worksheet ' 開けなかったブック名を出力するシート
Dim wsSrc As Worksheet ' 各転記元シート
Dim sFile As String ' 転記元の各ブック名
Dim i As Long ' ループ用
Dim cnT As Long ' 正しく出力できた数
Dim cnF As Long ' 転記元ブックをOpen出来なかった数
Dim flgO As Boolean ' 各ブックが実行前から開いていたかどうか
Set wsPrint = ThisWorkbook.Sheets(1) ' 転記先シート
Set wsLog = ThisWorkbook.Sheets(3) ' 開けなかったブック名を出力するシート
Application.ScreenUpdating = False ' 画面更新を一時停止
Application.EnableEvents = False ' イベントを一時抑止
cnT = 0: cnF = 0
For i = 0 To UBound(arrFn())
flgO = False
Set wsSrc = Nothing
sFile = arrFn(i)
' ' ーーーー転記元ブック開いている、と仮定して
' ' ーーーー転記元シートにアクセスしてみる
On Error Resume Next
Set wsSrc = Workbooks(sFile).Worksheets(1)
On Error GoTo 0
' ' ーーーー転記元シートへのアクセスに失敗していたならば
If wsSrc Is Nothing Then
' ' ーーーー転記元ブックはパスワード指定なしで開ける、と仮定して
' ' ーーーー転記元シートにアクセスしてみる
On Error Resume Next
Set wsSrc = Workbooks.Open(sDir & "\" & sFile, Password:="", UpdateLinks:=False, ReadOnly:=True).Worksheets(1)
On Error GoTo 0
Else
' ' ーーーー転記元シートへのアクセスに成功していたならば
' ' ーーーー転記元ブックは実行前から開いている
flgO = True
End If
' ' ーーーー転記元シートへのアクセスに失敗していたならば
If wsSrc Is Nothing Then
cnF = cnF + 1
' ' ーーーー開けなかったブック名を出力
wsLog.Cells(cnF, 1).Value = sFile
Else
' ' ーーーー転記元シートへのアクセスに成功していたならば
With wsSrc ' 転記元シート
cnT = cnT + 1
' ' B2の値、転記元の各ブック名、転記元の各シート名、を纏めて出力
wsPrint.Cells(cnT, "A").Resize(, 3).Value = Array(.Range("B2"), .Parent.Name, .Name)
' ' 元々開いていなかったブックならば保存せず閉じる
If Not flgO Then .Parent.Close False
End With
End If
Next i
Set wsPrint = Nothing: Set wsLog = Nothing: Set wsSrc = Nothing
Application.EnableEvents = True ' イベント抑止を解除
Application.ScreenUpdating = True ' 画面更新停止を解除
MsgBox UBound(arrFn()) + 1 & "個中 " & cnT & "個取得 " & cnF & "個失敗"
Erase arrFn()
End Sub
cj_moverさん、何度もありがとうございます。
> 転記元となるべきブックが実行前に既に開いていたとして、そのブックに未保存データがある場合、どうしようか、とか。
未保存データのようなことはまったく想定していませんでした。
危うくとんでもないものを作ってしまうところでした。
そのような場合の対応を私が決めるわけにもいかないので、とりあえずは
For Each wb(1) In Workbooks
If wb(1).Name <> ThisWorkbook.Name And Not StrConv(wb(1).Name, vbUpperCase) Like "PERSONAL.XLS*" Then
MsgBox "他のBookが開いているようです。" _
& vbCrLf & "お手数ですが、一旦他のBOOKを閉じてから開始してください。", vbCritical
Exit Sub
End If
Next wb(1)
で、逃げることにします。(個人用マクロBOOKの存在を考慮したつもりです)
ご指導有難うございました。
No.2
- 回答日時:
こんにちは。
お邪魔します。対策としては、
Openn メソッドの引数として、Password:="" を指定すること
On Error ステートメントから、Err オブジェクトを問い合わせて分岐
という2点です。
下に示した例では、
・変更点を◆マークで、こちらで一例として示している点を▲マークでそれぞれ示しています。
・「Openメソッドが失敗した場合」の処理がシンプルですので、On Error Resume Nextを使います。
・例として「Openメソッドが失敗した場合は」という意味で
If Err.Number <> 0 Then '▲例えばエラーならすべて
のように書いています。
If Err.Number <> 1004 Then
と書くと、「Excelワークブックの属性が原因でOpenメソッドが失敗した場合は」という意味になります。
Err.Number = 1004 に加えて、Err.Descriptionを判別に加えれば、
「パスワード指定漏れに因ってOpenメソッドが失敗した場合は」という意味に多少近付けるようですが、
ぴったりとしたものはすぐには思い付かず、あまり考えてもいません。。
「Openメソッドが失敗した場合は」という判別の方が実践的であろうと思っています。
・例としてThisWorkbook.Sheets(3)のA列に、開けなかったブック名を出力します。
書き振りに一貫性を持たせるなら、
ThisWorkbook.Sheets(3)を変数に格納したり、「開けなかったブック」をカウントすることになるのでしょうけれど、
特に手を加えてません。
自分なら、オブジェクトの扱いとして変数を用いるのは
Dim wsPrint As Worksheet ' 転記先シート Set wsPrint = ThisWorkbook.Sheets(1)
Dim wsLog As Worksheet ' 開けなかったブック名を出力するシート Set wsLog = ThisWorkbook.Sheets(3)
ぐらいで、後はすべてWithフレーズで済ませるように書くことが多いです。
・Application.EnableEvents がループの内にあることの意図が判らなかったのですが、一応、外に出しました。
・この手の処理でFolderPickerを使ってブックを開く場合は、
ThisWorkbook(の名前)を処理から除外しておいた方が安全ではあります。(コードは示しません)
同様にワークブックの二重起動についても考慮に入れた方がより無難になるかも、です。
Sub Re8470695()
Dim wb(1) As Workbook
Dim ws(1) As Worksheet
Dim myFdr As String, fn As String
Dim i As Long
With Application.FileDialog(msoFileDialogFolderPicker) 'フォルダ指定
If .Show = True Then
myFdr = .SelectedItems(1)
Else
Exit Sub
End If
End With
Set wb(0) = ThisWorkbook 'このコピー先ブックをwb(0)とする。
Set ws(0) = wb(0).Sheets(1) 'wb(0)の1枚目のシートをws(0)とする。
fn = Dir(myFdr & "\*.xls*") 'フォルダ内のExcelブックを検索
Application.ScreenUpdating = False '画面更新を一時停止
Application.EnableEvents = False '◆
Do Until fn = Empty '全て検索
On Error Resume Next '◆
Set wb(1) = Workbooks.Open(myFdr & "\" & fn, Password:="", UpdateLinks:=False, ReadOnly:=True) '◆そのブックを開きwb(1)とする。
If Err.Number <> 0 Then '▲例えばエラーならすべて
wb(0).Sheets(3).Cells(Rows.Count, 1).End(xlUp).Offset(1).Value = fn '▲例えばwb(0).Sheets(3)のA列に出力
Else '◆
Set ws(1) = wb(1).Worksheets(1)
i = i + 1
ws(0).Cells(i, "A").Value = ws(1).Range("B2") 'ws(0)に転記
ws(0).Cells(i, "B").Value = wb(1).Name
ws(0).Cells(i, "C").Value = ws(1).Name
wb(1).Close (False) '保存せず閉じる
End If '◆
On Error GoTo 0 '◆
fn = Dir 'フォルダ内の次のExcelブックを検索
Loop '繰り返す
Application.EnableEvents = True '◆
Application.ScreenUpdating = True '画面更新停止を解除
MsgBox i & "個取得"
End Sub
cj_moverさん、いつもありがとうございます。
なるほど、このようなやり方なんですね、初めて知りました!
> ThisWorkbook(の名前)を処理から除外しておいた方が安全ではあります。(コードは示しません)
以下のようにしてみましたが、よろしいでしょうか?
> 同様にワークブックの二重起動についても考慮に入れた方がより無難になるかも、です。
なるほど、すでに開いていることも考えられますね。
残念ながら、この対応法がわかりません。
Sub Re8470695()
Dim wb(1) As Workbook
Dim ws(2) As Worksheet
Dim myFdr As String, fn As String
Dim i As Long
With Application.FileDialog(msoFileDialogFolderPicker) 'フォルダ指定
If .Show = True Then
myFdr = .SelectedItems(1)
Else
Exit Sub
End If
End With
Set wb(0) = ThisWorkbook 'このコピー先ブックをwb(0)とする。
Set ws(0) = wb(0).Sheets(1) 'wb(0)の1枚目のシートをws(0)とする。
Set ws(2) = wb(0).Sheets(3) 'wb(0)の3枚目のシートをws(2)とする。
fn = Dir(myFdr & "\*.xls*") 'フォルダ内のExcelブックを検索
Application.ScreenUpdating = False '画面更新を一時停止
Application.EnableEvents = False '◆
Do Until fn = Empty '全て検索
If fn <> wb(0).Name Then
On Error Resume Next '◆
Set wb(1) = Workbooks.Open(myFdr & "\" & fn, Password:="", UpdateLinks:=False, ReadOnly:=True) '◆そのブックを開きwb(1)とする。
If Err.Number <> 0 Then '▲例えばエラーならすべて
ws(2).Cells(Rows.Count, 1).End(xlUp).Offset(1).Value = fn '▲wb(0).Sheets(3)のA列に出力
Else '◆
Set ws(1) = wb(1).Worksheets(1)
i = i + 1
ws(0).Cells(i, "A").Value = ws(1).Range("B2") 'ws(0)に転記
ws(0).Cells(i, "B").Value = fn
ws(0).Cells(i, "C").Value = ws(1).Name
wb(1).Close (False) '保存せず閉じる
End If '◆
On Error GoTo 0 '◆
End If
fn = Dir 'フォルダ内の次のExcelブックを検索
Loop '繰り返す
Application.EnableEvents = True '◆
Application.ScreenUpdating = True '画面更新停止を解除
MsgBox i & "個取得"
End Sub
No.1
- 回答日時:
ちょっと試した限りでは・・・、
パスワードの設定を一切行っていないファイルを
set bk=workbooks.open("e:\boo.xlsx",password="yomi",writerespassword:="kaki")
パスワードの部分は無視されて開くようです。
一方どちらかにパスワードの設定がある場合
set bk=workbooks.open("e:\boo.xlsx",password="",writerespassword:="")
では、実行時エラー 1004 になりましたので
エラー処理で行うとかでは?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) ExcelVBAに関する質問 3 2023/02/17 10:47
- Visual Basic(VBA) 【前回の続き続きです、ご教示ください】VBAの記述方法がわかりません。 2 2022/08/24 20:49
- Visual Basic(VBA) vbaのエラー対応(実行時エラー7:メモリが不足しています) 4 2023/04/24 00:20
- Visual Basic(VBA) 【ご教示ください】VBAの記述方法がわかりません。 2 2022/08/12 21:28
- Visual Basic(VBA) 【前回の続きです、ご教示ください】VBAの記述方法がわかりません。 2 2022/08/16 16:44
- Visual Basic(VBA) VBAが止まります。 2 2022/09/02 14:02
- Visual Basic(VBA) Excel vbaについての質問 3 2023/04/18 16:14
- Visual Basic(VBA) VBAのユーザーフォームのテキストボックスに入力制限をしたい 6 2022/11/15 08:28
- Visual Basic(VBA) VBA This Workbookモジュールを別ファイルにコピーする方法 1 2022/09/14 01:51
- Visual Basic(VBA) エクセル VBAについて教えてください 2 2023/04/26 13:25
このQ&Aを見た人はこんなQ&Aも見ています
-
VBA/エクセルの日付入力でYYYYMMDD
Excel(エクセル)
-
エクセルのラベルの値(文字列)を垂直方向で中央揃えにするには?
Excel(エクセル)
-
エクセルVBAでRangeの引数制限?
Excel(エクセル)
-
-
4
Workbook_Openを起動時以外に呼び出す
Excel(エクセル)
-
5
エクセルVBAでOutlookメールの書式を変える
Excel(エクセル)
-
6
VBAでオブジェクトがありません、となってしまう
Visual Basic(VBA)
-
7
エクセル2010でグラフのリンクを削除
Excel(エクセル)
-
8
エクセルVBA 他の仕事を止めない時間稼ぎ
Visual Basic(VBA)
-
9
パスワード入力画面をスキップするために(AccessVba)
Excel(エクセル)
-
10
「timer」の不思議
Excel(エクセル)
-
11
VBA データ(特定値)のある最終行を取得したい
Excel(エクセル)
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
VBA シートをコピーする際に Co...
-
VBA 別ブックからコピペしたい...
-
別ブックをダイアログボックス...
-
ワイルドカード「*」を使うとう...
-
エクセルVBAが途中で止まります
-
【前回の続きです、ご教示くだ...
-
VBAで別ブックのシートを指定し...
-
VBAで別のブックにシートをコピ...
-
VBA 実行時エラー 2147024893
-
Excel にて、 リストボックスの...
-
【マクロ】アクティブセルにブ...
-
[Excel]ADODBでNull変換されて...
-
マクロVBA別Excelブックにデー...
-
エクセルのマクロについて教え...
-
【ExcelVBA】zip圧縮されたCSV...
-
【Excel VBA】書き込み先ブック...
-
フォルダ内の全てのファイルに...
-
ACCESSVBA からExcelの他ブック...
-
Excelブックがアクティブになっ...
-
エクセルVBAでテキストボックス...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VBA シートをコピーする際に Co...
-
別ブックをダイアログボックス...
-
VBA 別ブックからコピペしたい...
-
エクセルVBAが途中で止まります
-
ワイルドカード「*」を使うとう...
-
VBA コードを実行すると画面が...
-
VBAで別のブックにシートをコピ...
-
VBAで別ブックのシートを指定し...
-
【Excel VBA】書き込み先ブック...
-
Excelマクロ 該当する値の行番...
-
【ExcelVBA】zip圧縮されたCSV...
-
[Excel]ADODBでNull変換されて...
-
【ExcelVBA】インデックスが有...
-
Excel2007VBAファイルの表示に...
-
VBAで複数のブックを開かずに処...
-
vbaで他のブックに転記したい。...
-
エクセルマクロで、他ブックか...
-
vbaでvbaProjectのパスワード解...
-
VBA 実行時エラー 2147024893
-
【マクロ】違うフォルダにある...
おすすめ情報