こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

ExcelVBA:すでに開かれているブックの判定方法

同 Excel内で開かれている異なる複数のBOOKの判断方法は以下のサイトを参考にできました。
http://www.asahi-net.or.jp/~ef2o-inue/vba_o/sub05_010_020.html

しかし、別のEXCELを起動して開いているBOOKについては、上記のコードの判断にはかかりませんでした。
別のEXCELを起動して開いているBOOKについても、すでに開いていることを判断したいのですが、何か方法がございますでしょうか?

よろしくお願い致します。

投稿日時 - 2007-07-24 16:30:41

QNo.3195626

困ってます

質問者が選んだベストアンサー

> やはり別プロセスのシートを複数選択して、まとめて列挿入する
> ことは無理なんですね。

いえ、無理ではないでしょう。#7 は #6 補足欄のコードをなるべく
生かしました。それだけです。参考コードを示しておきます。

  Dim wb    As Workbook
  
  Set wb = GetObject("C:\test.xls")
  
  wb.Sheets(Array("Sheet1", "Sheet2", "Sheet3")).Select
  With wb.Sheets("Sheet1")
    .Activate
    .Range("A1:A10").Select
  End With
  
  With wb.Application.Selection
    .Value = "TEST STRING"
    .Insert shift:=xlToRight
  End With

> ただ、まだ別プロセスのシート内を検索するのに上手く参照できません。
> Set r = sh.Cells.Find(What:=s_EncodeFile_nm, _
> After:=Range(s_myFadd), _
> LookIn:=xlValues, _
> LookAt:=xlWhole, _

これだけ見せられても何とも言いがたいのですが、Range(s_myFadd) は
#5 回答が参考にされてませんね^^;

親オブジェクトの装飾が必要かと。

本来のご質問からは完全に別の問題になってしまいますので、これ以上
何か不明点があるのであれば、別スレッドを立てた方がよろしいかと
思います。

申し訳ありませんが、私もこれで最後にします。

投稿日時 - 2007-07-31 13:16:22

お礼

大変失礼しました。
すでに可動させているため、なかなかコード手を加えにくい状況に至っておりました。
次改修の際に、今までいただいたコードをゆっくりと見直し、改良させていただこうと思っています。
また、新たな課題にぶつかった際には、あらためて別途、質問を投稿させていただきたいと思います。

本来のご質問から離れてしまったのに、長々とお付き合いいただき、ありがとうございました。

投稿日時 - 2007-07-31 17:33:54

このQ&Aは役に立ちましたか?

2人が「このQ&Aが役に立った」と投票しています

回答(8)

ANo.7

質問がふくらみ過ぎかと。。きりがないのでサンプルコードを示して
おきます。なるべく平易なコードを心がけ、コメントをたくさん入れ
ました。分かり難い部分はあると思いますが、試行錯誤を繰り返して
ご自分のものとされるように。。

僭越ながらひとつアドバイスを差し上げます。

このようなサンプルコードがあったとして、それを実行し、結果を
眺めているだけでは、結局ご自分の力にはなりません。他人が書いた
コードはきまって難解ですしね。

他人のコードを読む時は、「ステップ実行」してみるんです。

VBE で実行したいプロシージャの中にカーソルをおき、F8 キーを
押します。すると、コードが一行実行されます。一行実行される度に
中断されます。また F8 を押すと、次行が実行されます。

こうすると、For ~ Next、If ~ End If などの制御構文の流れや、
Call などでサブルーチンへジャンプする、、などコードのフローが
視覚的に読めます。

この時、VBE のローカルウインドウを表示しておくのがコツです。
コードの中断時における変数の値がすべてチェックできますよ。

こうやって、ひとつひとつ順に追いながら、そのコードにどのような
意味があったのかを読み解いていきます。

まあ、、、騙されたと思ってやってみて下さい。コードの意味が正確
に全てわからなくても OK。とにかく少しずつでもいいから、

1. コードを一行見て、その動作を想像してみる
2. F8 を押して、その一行を実際に実行
3. どのような結果になったかをローカルウインドウやシート・セル
  を見て確認する

これを繰り返すと良いですよ。


Sub Main()

  Dim wb    As Workbook
  Dim sh    As Worksheet
  Dim sFilePath As String ' // 変数名先頭の s は String の略です
  Dim vShNames As Variant ' // 変数名先頭の v は Variant の略です
  Dim vItem   As Variant
  
  ' // 対象ファイルフルパス
  sFilePath = "C:\test.xls"
  
  ' // ここの解説は既にしてある
  On Error Resume Next
  Set wb = GetObject(sFilePath)
  On Error GoTo 0
  
  If wb Is Nothing Then
    MsgBox "Not Found Err: " & sFilePath, vbCritical
    Exit Sub
  Else
    ' // 文字列をカンマ区切りで配列に分解する
    vShNames = Split("Sheet1,Sheet2,Sheet3", ",")
    ' // 配列の中身をひとつずつループでまわす
    For Each vItem In vShNames
      ' // シート名は変数 vItem にある
      ' // ただし、そのシートがないかもしれないので、エラー処理を行う
      On Error Resume Next
      ' // 変数 sh に 変数wb(ブック)配下のシート(vItem)を参照してみる
      Set sh = wb.Worksheets(vItem)
      ' // エラー処理無効化(これ以降は不必要なので)
      On Error GoTo 0
      ' // 参照に失敗=シート(vItem)がなければ、変数 sh は Nothing
      If sh Is Nothing Then
        ' // シートが見つからない場合-->次のシートへ
        MsgBox "Not Found Err: Worksheet[" & vItem & "]", vbCritical, "Skip"
      Else
        ' // シートが見つかった場合
        ' // 名前を引数にするのではなく、参照した Worksheet オブジェクト、
        ' // つまり sh を引数にすると良い
        Call RowLineAdd(sh)
        Set sh = Nothing
      End If
    Next
  End If

End Sub

Sub RowLineAdd(ByVal sh As Worksheet)
  
  ' // 概ねこのようにまとめられると思う
  sh.Columns("A:A").Insert Shift:=xlToRight
  With sh.Columns("A:A")
    .Style = "Normal"      ' // 標準書式に戻す
    .Font.ColorIndex = 3    ' // フォント色を赤色に
    .Font.Bold = True      ' // フォントを太字に
    .NumberFormatLocal = "@"  ' // 表示形式を文字列に
  End With
  With sh.Range("A1").Borders
    .LineStyle = xlContinuous
    .Weight = xlThin
    .FormulaR1C1 = "追加した列に付ける名前"
  End With

End Sub

投稿日時 - 2007-07-28 01:21:41

お礼

やはり別プロセスのシートを複数選択して、まとめて列挿入することは無理なんですね。
ループで1シートづつ列挿入させることで実現できました。

ただ、まだ別プロセスのシート内を検索するのに上手く参照できません。
Set r = sh.Cells.Find(What:=s_EncodeFile_nm, _
After:=Range(s_myFadd), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
MatchByte:=False)
な感じで参照しているのですが・・・

別プロセスのExcelをコントロールすることは初めての試みなので、
なかなか難しいですが、おかげさまで一つ一つ課題クリアできました。
ありがとうございました。

投稿日時 - 2007-07-31 11:47:19

ANo.6

ああ、、肝心なことを忘れてた。

#5 と完全に 180 度違うこと言いますが、

 ・ファイルが使用されていたら、無理に処理を続行せず、
  メッセージを表示して、ファイルを閉じてもらうよう
  ユーザーを促す

というのが仕組みとして一番シンプルです。

そして、ユーザーに閉じてもらったら、VBA でそのブックを
開き直せば、全て同一プロセスに属するのですから、コード
は簡単ですし、既存コードの手直しも不要です。

投稿日時 - 2007-07-26 18:00:43

補足

ありがとうございます。
おっしゃるとおり、ウォーニングメッセージなどを出して、
同一プロセス内で開いてもらえるようにすればいいのですが。。

通常は、開いているEXCELがあって、
さらに他のEXCELファイルをクリックすると、
デフォルトで同一プロセス内で開くようになっています。

が、、、実は当職場の都合上で、
多くのWindows環境を以下のように設定しているのです。
http://okwave.jp/qa3141260.html
(自分の過去の質問により解決した内容です)

これにより、他の作業者は、
別プロセスで開かれるのがデフォルトとなっています。

よって、どちらのプロセスで開かれていても、
対応できるようにコードを処理させてあげなければ・・・
と考えている次第なんです。

投稿日時 - 2007-07-26 18:21:43

ANo.5

> 元EXCEL(VBAコードが記述されているEXCEL)の
> Range("A2").Offset(i, 0).Valueを参照してしまうようです。

そうなるはずです^^;

プロセスが異なる場合は、同一プロセスのブックやセルを操作
するほど簡単ではありません。
CetObject は何もブックを Active にするためだけに使われた
のではありません。簡単なサンプルコードを書きましょう。

Sub Sample()

  Dim wb As Workbook
  Dim sh As Worksheet
  
  Set wb = GetObject("C:\test.xls") '// ブックが参照される
  Set sh = wb.Worksheets("Sheet1")  '// ワークシートが参照される
  
  sh.Range("A1").Value = "TEST"
  
  Set sh = Nothing
  Set wb = Nothing

End Sub

このように GetObject で test.xls を捕らえた場合、test.xls
を操作するコマンド全てに親オブジェクトの装飾が必要です。
例えば、セル A1 の操作なら

 [ブック]-[シート]-[セル]

のように、上から下のオブジェクトへと順に追っていかなければ
なりません。

この辺は、経験が少ないと理解しがたいと思います。きっと大変
でしょうが、頑張って試行錯誤して下さい。覚えて損はありません。
これが、発展すると Excel VBA を使って Word、Access、など
別アプリケーションと連携するコードが書けるようになりますよ。

投稿日時 - 2007-07-26 17:53:08

補足

ありがとうございます!
おかげさまで、上手く動作させることができました!

でも、、、また一つ問題が。。。
すいません。

ANo.4の補足のところで、
<処理概要>
xxxxx.xlsのn行目aセルにある値を、yyyyy.xlsのシートから検索し見つかったら、
xxxxx.xlsのn行目bセルにある値を、yyyyy.xlsのシートへコピー(検索し見つかったセル+c列へ)
それを、n行分を繰り返す。
と、書かせていただきましたが、
「yyyyy.xlsのシートへコピー(検索し見つかったセル+c列へ)」
のところですが、検索対象のyyyyy.xlsのシートは8つほどあり、
検索し見つかったセルの値を、コピーするための列ですが、
実際は「検索し見つかったセル+c列」ではなく、
検索し見つかったセルの“一番左端の列”にコピーしています。
そのために、あらかじめ8つのシートの一番左端列にあらたな列を挿入すべく、
yyyyy.xlsを開いた後(またはすでに任意で開かれている)に
別のサブルーチンで、まとめてシート8つ分に1列挿入する処理をしています。

問題というのは、yyyyy.xlsが、別EXCELで開かれている場合、
サブルーチン(RowLineAdd)の一行目“Sheets(v_Sheet_nm).Select”で、
エラーとなってしまいます。
これも、別プロセスのため、エラーになってしまうのだと思うのですが、
サブルーチン内において、別プロセスのEXCELシートをコントロールしたいのですが、
サブルーチン内においても、やはり以下のように指定できるようする必要がありますか?

Dim sh As Worksheet
Set sh = wb.Worksheets(v_Sheet_nm).Select
Set sh = wb.Worksheets("シート名1").Activate

それとも、もっと簡単なサブルーチン内での指定方法はありますでしょうか?



-----現在のコード-----
Sub Main()
 ・
v_Sheet_nm = Array("シート名1", "シート名2", "シート名3", "シート名4", "シート名5", "シート名6", "シート名7", "シート名8")
 ・
 ・
 ・
Call RowLineAdd(v_Sheet_nm)
End Sub


Sub RowLineAdd(v_Sheet_nm As Variant)
Sheets(v_Sheet_nm).Select
Sheets("シート名1").Activate
Columns("A:A").Select
Selection.Insert Shift:=xlToRight
Columns("A:A").Select
With Selection.Font
.Name = "MS Pゴシック"
.Size = 11
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
End With
Selection.Font.ColorIndex = 3
Selection.Font.Bold = True
Selection.NumberFormatLocal = "@"
Range("A1").Select
Selection.Borders(xlDiagonalDown).LineStyle = xlNone
Selection.Borders(xlDiagonalUp).LineStyle = xlNone
With Selection.Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
With Selection.Borders(xlEdgeTop)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
With Selection.Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
With Selection.Borders(xlEdgeRight)
.LineStyle = xlContinuous
.Weight = xlThin
.ColorIndex = xlAutomatic
End With
ActiveCell.FormulaR1C1 = "追加した列に付ける名前"
Range("A2").Select
End Sub

投稿日時 - 2007-07-27 16:18:56

ANo.4

#2 のコードは、

1. 自分のPCで開いている(別プロセスの Excel.exe を含む)
2. ネットワーク経由の他 PC で開いている
3. 可能性は低いものの、Excel.exe 以外のアプリケーションで
  開かれている。

この3つの違いまで区別するものではありません。

■ 1. について
自プロセスの場合は簡単ですね。別プロセスの Excel.exe の場合、
事前にフルパスさえ判明していれば #3 ご回答の GetObject が
使えます。

■ 2~3 について
検証する時間が取れないので、その点を前置きしておきますが、
GetObject は別プロセスの Excel.exe 内であっても「開いて」さえ
いれば目的のオブジェクトを期待どおり取得できますが、2~3. の
場合では、例えファイルが使用中であっても

  「読み取り専用で開いてしまう」

...はずです(確かね...)。そうなると、ご質問の

> すでに開いていることを判断したい

というのが、「判断した後、どうしたいのか?」によって回答内容
が変わってきそうです。

投稿日時 - 2007-07-25 23:55:26

補足

ありがとうございます!

>この3つの違いまで区別するものではありません。
はい、わかりました。
動作環境は、1.で、すべてEXCELアプリです。

ちなみに、以下の感じで動作させています。

<処理概要>
xxxxx.xlsのn行目aセルにある値を、yyyyy.xlsのシートから検索し見つかったら、
xxxxx.xlsのn行目bセルにある値を、yyyyy.xlsのシートへコピー(検索し見つかったセル+c列へ)
それを、n行分を繰り返す。

(1)元EXCEL(VBAコードが記述されている)で、(2)と(3)のファイル名をあらかじめセルに記述し、そのファイル名(2)(3)を処理対象としてコードが実施される。

(2)例としてxxxxx.xls
パターン1:同EXCEL内(元EXCEL内)にて開らき、処理対象とする
パターン2:すでに任意で開かれている(別EXCELで)場合、そのままで処理対象とする
パターン3:すでに任意で開かれている(同EXCEL内(元EXCEL内)で)場合、そのままで処理対象とする

(3)例としてyyyyy.xls
パターン1:同EXCEL内(元EXCEL内)で開らき、処理対象とする
パターン2:すでに任意で開かれている(別EXCELで)場合、そのままで処理対象とする
パターン3:すでに任意で開かれている(同EXCEL内(元EXCEL内)で)場合、そのままで処理対象とする

投稿日時 - 2007-07-26 17:12:42

ANo.3

No1です。No2さんの方法でできるようですのでもういいとは思いましたが、誤解があるようですので補足しますね。
>オープンされていないときは、リネームされてしまうので、また元名でリネームし直してやる必要がありそうですね。
→同名でのリネームなので、リネームし直す必要はありません。

そして、横から失礼します。
>別エクセルで開いている場合、そのエクセルファイルの方>(xxxxx.xls)をアクティブにさせる場合は、
>どういう指定をしなければならないんでしょうか?
↓こんな感じでどうでしょう?
Dim xlBook As Workbook
Dim strFilePath As String

strFilePath = xxxxx.xlsのフルパス

'xxxxx.xlsのブックを取得
Set xlBook = GetObject(strFilePath)

'Excel内でxxxxx.xlsをアクティブブックにする
xlBook.Activate

'Excelのウィンドウをアクティブウィンドウにする
Call AppActivate(xlBook.Application.Caption)

投稿日時 - 2007-07-25 21:29:13

補足

わざわざ、ありがとうございます。

>同名でのリネームなので、リネームし直す必要はありません。
そういうことでしたか、すいません!
気づきませんでした。

>こんな感じでどうでしょう?
早速、用いてみたところ、上手くアクティブにできました!
別EXCELで開いているかどうか判定し、
開いている場合は、いただいたコードでアクティブにします。
助かりました、ありがとうございます!

で、また問題が・・
実は“xxxxx.xls”が、別EXCELで開かれている場合、
“xxxxx.xls”をアクティブにして、
“xxxxx.xls”のRange("A2").Offset(i, 0).Value
を取ろうとしたところ、元EXCEL(VBAコードが記述されているEXCEL)
のRange("A2").Offset(i, 0).Valueを参照してしまうようです。

同EXCEL内で“xxxxx.xls”が開かれている場合は、
“xxxxx.xls”をアクティブにすると
Range("A2").Offset(i, 0).Value
で、“xxxxx.xls”のシートセル上のデータが拾えたのですが・・

そこで、もし、ご存知であれば教えていただきたいのですが、
“xxxxx.xls”が、別EXCELで開かれている場合、
“xxxxx.xls”をアクティブにした後、
“xxxxx.xls”のRange("A2").Offset(i, 0).Value
を取りたい場合、どういう参照の仕方をしなければならないのでしょうか?

当初の質問から発展してしまい、すいません。
当初の問題が解決されることで、今まで上手く動作(または参照)していた、後半のコードに支障が出てしまって・・
m(_"_)m

投稿日時 - 2007-07-26 16:32:41

ANo.2

' // 使い方
Sub Sample()

  Select Case IsFileEditable("C:\test.xls")
    Case -1: MsgBox "ファイルが見つかりません", vbCritical
    Case 0: MsgBox "ファイルは使用中です", vbExclamation
    Case 1: MsgBox "ファイルは使用可能です", vbInformation
  End Select
  
End Sub

' // ファイルが編集可能か調べるユーザー関数
Public Function IsFileEditable( _
    ByVal FilePath As String _
) As Long

  ' // 戻り値:Long -1:ファイルなし 0:ファイルは使用中 1:ファイルは使用可能

  Dim n  As Integer
  If Len(Dir$(FilePath)) = 0 Then
    IsFileEditable = -1
    Exit Function
  End If
  n = FreeFile()
  On Error Resume Next
  Open FilePath For Binary Lock Read Write As #n
  Close #n
  IsFileEditable = IIf(Err.Number = 0, 1, 0)
  On Error GoTo 0

End Function

投稿日時 - 2007-07-25 14:15:51

補足

そのファイルが編集可能かどうかでオープンされているかされていないかを判断させるわけですね。
ありがとうございます!
コードまでいただき、助かります!

上手く判定させることができたのですが、
もう一つ問題が発生しました。。
もし、ご存知であれば、ご教授下さい。

別エクセルで、すでに開いている“xxxxx.xls”ファイルがあるとして、
Workbooks("xxxxx.xls").Activate
で、その“xxxxx.xls”ファイルの方をアクティブさせようとしたところ、
「実行時エラー'9':インデックスが有効範囲にありません。」
とのエラーが出てしまいました。
同エクセル内で、“xxxxx.xls”ファイルを開いている場合は、
Workbooks("xxxxx.xls").Activate
で、アクティブに出来ていたのですが、
別エクセルで開いている場合、そのエクセルファイルの方(xxxxx.xls)をアクティブにさせる場合は、
どういう指定をしなければならないんでしょうか?

よろしくお願い致します。

投稿日時 - 2007-07-25 16:58:59

ANo.1

事前にファイルの存在チェックとか、事前チェックが必要とは思いますが通常は以下のようにすると判定できると思います。
読み取り専用属性が付いていたりすると、以下の方法では判定できません。
その場合は・・・。すみません。わかりません。
------------------------------------------------------
Dim strFilePath As String

'ファイルパス
strFilePath = "C:\tmp\test.xls"

On Error Resume Next
'同名でリネームしてみる
Name strFilePath As strFilePath
If Err.Number = 0 Then
'リネーム可能
MsgBox "オープン中されてない"
Else
'リネーム不可
MsgBox "オープン中"
End If
On Error GoTo 0

投稿日時 - 2007-07-24 21:11:02

お礼

ご回答ありがとうございました。
なるほど・・リネームをかけて結果で判定するということですね。
オープンされていないときは、リネームされてしまうので、また元名でリネームし直してやる必要がありそうですね。

投稿日時 - 2007-07-25 10:28:23

あなたにオススメの質問