dポイントプレゼントキャンペーン実施中!

エクセル2007のマクロを修正しています。
ソルバで、オプションの制限時間を設定しており、
制限時間に達した時に、「試行状況の表示」が現れるため、
マクロが途中で停止してしまい、困っております。

「試行状況の表示」が現れる時に、中止(T)を選択できれば、
目的のマクロになります。いくつかの方法を試したのですが、
うまくマクロが動作しません。

【試した事】
 (1)Dummy Macro() → 「値の更新」が表示されて止まる。
 参考 http://oshiete.goo.ne.jp/qa/2630207.html

 (2)Function → 「値の更新」が表示されて止まる。
 参考 http://msdn.microsoft.com/ja-jp/library/office/f …

 (3)Application Display Alerts → 「試行状況の表示」が表示されて止まる。
 参考 http://oshiete.goo.ne.jp/qa/2630207.html

コードを記述しないと、非常に回答し辛い事は、承知しております。
ただ、セキュリティの問題で、生コードは持ち出せません。また、
現在、マクロを勉強中のため、簡易的に書く技術が無く申し訳ございません。

やりたい内容や課題になっている所は、gucchi-you殿の質問と同様だと思います。
http://oshiete.goo.ne.jp/qa/2630207.html
この質問は、解決せずに終了しております。

2週間試行錯誤しましたが、うまくいかずに投稿させて頂きました。
もし、ご回答頂ければ、非常にありがたいです。
お知恵をお貸し下さい。

A 回答 (6件)

質問者様と似たようなトラブルを解決した事例が見つかり、そこでのトラブルの原因を取り入れてやってみたところ、場合によっては「値の更新」ダイアログが出るようになりました。



原因は、ファイル名にスペースやハイフンなどが入っていることです。
ソルバーにはいまだにExclel4.0マクロが使われている(ことを今回初めて知った)のですが、そこでスペースやハイフンなどが入っているブック名の処理がうまくいかないようです。
(SolverSolveをステップ実行するとある程度はわかります)

よって、ファイル名(ブック名)にスペース、ハイフンその他かっこ等の文字が入らないようにすれば解決する可能性があります。
英字全半、数字全半、アンダースコア全半、ひらがな、カタカナ全半、漢字は大丈夫なようです。
逆にスペース等は全角でもだめです。

情報元は以下のところです。
http://www.mrexcel.com/forum/excel-questions/683 …


ついでに付け加えると、ShowRefで指定する関数名がA1やR2C3などセル範囲と誤解されるようなものだとエラーが発生します。セル範囲の名前があればそれも同様です。
ただしこちらはモジュール名をつければ回避できます。
たとえば関数A1がModule1にあるなら、以下のようにします。
SolverSolve UserFinish:=True, ShowRef:="Module1.A1"
この件は、まだよくわからないうちに回答No.1の最後に書いたものですが、そのときはExcel2010固有の問題で"ShowRef1"という長い名前でも問題が発生する、と書きました。
しかしそれは正しくないようで、セルのアドレスやセル範囲の名前と同じ関数名が問題となるようです。

(なお、もし上記の情報で解決した場合でも、Sendkeysの情報をもご希望であるならば、質問を締め切らずにお待ちください)

参考URL:http://www.mrexcel.com/forum/excel-questions/683 …

この回答への補足

ご回答ありがとうございます。
教えて頂いた方法で、値の更新が出なくなりました。

原因は、ファイル名に”、”があったためでした。削除したら、マクロが動くようになりました。

ファイル名に使用禁止文字があるなんて、教えて頂かなければ、絶対に分かりませんでした。ありがとうございました。

Sendkeysの件、勉強のために、ぜひご教授の程宜しくお願いいたします。マクロが動作するようになったので、急ぎではありません。お時間がある時に、ご回答頂けるとありがたいです。

補足日時:2013/06/26 23:39
    • good
    • 0
この回答へのお礼

queuerev2様

色々と調べて頂き、さらに、詳しいサンプルコードでご説明をありがとうございました。また、すぐにご回答頂けて、非常にありがたかったです。
お陰様で、マクロが動くようになりました。動作は快調です。
原因が特定されて、問題が解消した回答のため、この回答をベストアンサーにしたいと思います。

お礼日時:2013/07/16 21:27

質問者様はExcel2007をお使いになっているとのことですが、Office2007は32bit版なのでこのままで大丈夫です。


(現状では2010以降でも32bit版を使っているケースが多いと思われます。なお、この話にはWindowsやCPUのbit数は直接は関係ありません。)

とはいえ、今後64bit版のOfficeへの対応が必要になるかもしれませんので方法を述べます。
標準モジュールに書く場合です。

#If VBA7 Then
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As LongPtr)
#Else
Public Declare Sub Sleep Lib "kernel32" (ByVal ms as Long)
#End If

office2007などでは2行目がエラーで赤くなりますがそれでOKです。
64bit版Officeでは逆に4行目がエラーになるらしいです。
Public はなくても同じですが書いた方がお行儀がいいので今後は書くことにしようと思います。
そのモジュールの中だけでSleepを使えるようにしたい場合やシートモジュールなどに書く場合はPrivateにします。

http://www.happy2-island.com/excelsmile/smile04/ …
http://support.microsoft.com/kb/983043/ja
http://answers.microsoft.com/ja-jp/office/forum/ …

参考URL:http://www.happy2-island.com/excelsmile/smile04/ …
    • good
    • 0
この回答へのお礼

queuerev2様

サンプルコードやご説明をありがとうございました。
時間がかかりましたが、内容が、分かりました。
これからも、マクロを勉強していきたいと思います。

お礼日時:2013/07/16 21:21

前の回答に書ききれなかったダイアログ操作用のコードです。


標準モジュールに貼り付け、ブックを KeySender1.xlsm として保存してください。
あとは1つ前の回答をご覧ください。

Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
'VBAで低CPU負荷で1ms単位で指定可能なSleepを使うための宣言
'64bit版Officeを使用している場合は要変更

Private TitleA As String, TitleD, Ks, RTime, FFP, FTime
'モジュール変数。 アプリケーションタイトル、ダイアログタイトル、
'送信キー、実行時間、フラグファイルパス、停止時刻

Const CC_FFP = 10 'フラグファイル確認1回あたりのウィンドウ探索回数
'フラグファイル確認はファイルアクセスなので回数を減らしたい

Const contTimeStr = "00:00:10"
'連続してウィンドウアクティブ化・キー送信しっぱなしになった際の停止タイマー
'連続送信で停止したくないなら"00:00:00"に

'本体起動用プロシージャ
'OnTimeによる本体の即時起動を仕掛けて呼び出し元に戻る
'引数は Title_Application アプリケーションタイトル、
'    Title_Dialog ダイアログタイトル、Keys 送信キー
' RunningTime 実行時間 0なら実行時間制限なし
' FlagFilePath フラグファイルパス
Sub Start_SKtoD(Title_Application, Title_Dialog, Keys, RunningTime, FlagFilePath)
TitleA = Title_Application
TitleD = Title_Dialog
Ks = Keys
RTime = RunningTime
FFP = FlagFilePath
FTime = RTime + Now()
Application.OnTime Now(), "Call_Sendkeys_to_Dialog"
End Sub

'OnTimeで呼び出されるプロシージャ
'本体を呼び出し、終了後Excelを終了する。
Sub Call_Sendkeys_to_Dialog()
Call Sendkeys_to_Dialog
Application.DisplayAlerts = False
Application.Quit
End Sub

'本体
Sub Sendkeys_to_Dialog()
Dim i, contTimer, contTime
Dim oWshShell
Dim oShell
Set oWshShell = CreateObject("WScript.Shell")
Set oShell = CreateObject("Shell.Application")

oWshShell.AppActivate TitleA 'ダイアログを出すアプリケーションをアクティブ化
DoEvents
Sleep 300
contTime = TimeValue(contTimeStr)
contTimer = Now()
Do '以下ループ内ではDoEventsやSleepを適当に入れてうまく動くように調整する
If RTime > 0 Then 'RTime <= 0なら終了時刻確認なし
If Now > FTime Then '終了時刻を過ぎたか?
Debug.Print "time over"
Exit Do '過ぎたら終了
End If
End If
i = i + 1 'フラグファイル確認用カウンタ
If i >= CC_FFP Then 'カウンタがCC\FFP以上?
If Len(Dir(FFP)) > 0 Then 'フラグファイルはあるか?
DoEvents
Kill FFP 'あるならファイルを削除して
Debug.Print "flagfile exists"
Exit Do '終了
End If
i = 0 'フラグファイルがないならカウンタリセット
End If
DoEvents
Sleep 200
If oWshShell.AppActivate(TitleD) Then 'ダイアログはあるか?
Debug.Print "dialog found.. ";
If contTimer > 0 And Now() - contTimer > contTime Then
Debug.Print "continuous act&key"
Exit Do 'キー送信連続時間超過なら終了。(contTimer=0で無制限)
End If
DoEvents
oWshShell.AppActivate TitleA 'ダイアログを出したアプリケーションをアクティブ化
'AppActivate TitleA 'ダイアログを出したアプリケーションをアクティブ化
DoEvents '十分にアクティブ化するまでDoeventsとSleepで待つ
Sleep 200
DoEvents
SendKeys Ks 'キー送信
DoEvents '送信したキーによる処理を進めるためにDoEvents
Else
contTimer = Now() 'キー送信連続開始時間更新
End If
Loop
End Sub

Sendkeysによるダイアログの操作は以上です。

最後に、解答No.1のコードの不具合にて少し述べさせていただきます。
解答No.1のコードは、ソルバーを適用するための数式をSheet1に書き込み、ソルバーはアクティブなシートで実行しています。そのため、Sheet1以外をアクティブにして動作させるとうまくいきません。
つまり、わざわざSheet1を指定したことが余計だったというわけです。
解決策としては、 S. をすべて削除するか、もっと簡単にはSet S = Sheets("Sheet1")をSet S = ActiveSheetにします。
当方の未熟さが出てしまい申し訳ありません。
    • good
    • 0

ファイル名を制限することで解決したのですね。


いろいろ調べたりした甲斐がありました。
しかし「、」もだめとはちょっとびっくりです。
Excel4.0マクロだけのせいかどうかはわかりませんが、やっかいな問題ですね。

それでは、ダイアログをSendkeysで操作する方法を解説いたします。

Sendkeysはキーボードのキーを押す代わりにプログラムからキーを送信する方法です。
人間がキーを押す場合は画面を見て確認しながら操作することができます。しかしVBAは画面の状態をほとんど確認することができませんし、ウィンドウの検出には通常はウィンドウタイトルを手掛かりにするので、タイトルの似ているウィンドウがあると誤検出することがあります。そのため、どうしても動作が不安定になる可能性を排除できません。後述のとおりサンプルを作ったもののそれほどよい出来ではないことも考え合わせ、やはり非常手段だという印象です。
それでも、Sendkeysなどユーザーインターフェースを操作する以外よい方法がないことも多く、いろいろなところで使われているようです。
なお、マウス操作も行いたい場合などは、UWSCというソフト(シェアウェア・フリーウェア)がよく使われます。
http://www.uwsc.info/

Sendkeysおよびウィンドウアクティブ化に使用するAppActivateの機能や使用法についてはWeb検索で多くの情報が得られると思いますが、とりあえずMSDNのリンクを載せておきます。
http://msdn.microsoft.com/ja-jp/library/office/g …
http://msdn.microsoft.com/ja-jp/library/office/g …
http://msdn.microsoft.com/ja-jp/library/cc364423 …
http://msdn.microsoft.com/ja-jp/library/cc364396 …
上記を見てお気づきかもしれませんが、VBAで使用可能なSendkeysとAppActivateとしては、VBAのステートメントとWshShellオブジェクトのメソッドの2種類があり、機能が微妙に異なります。(Sendkeysはもう1つApplicationのものあります)
今回はAppActivateでウィンドウの検出を行うことにしますので、その部分にはWshShellのメソッドを使うことになります。
もう1つ今回参考にした例を挙げます。(VBScriptですが)
http://www.atmarkit.co.jp/fwin2k/tutor/cformwsh1 …

ではダイアログ操作を解説します。どうやって解説しようか考えているうちに結局は一応動くものを作ってしまったのですが、自己流の部分もあり不適切なところもあるかもしれませんが、とりあえず動いた例として書かせて頂きます。

ExcelにおいてSendkeysでダイアログを操作するという話題で、印刷設定を行うというものがあります。そういうコードを見ると、With AcitveDialogの中でダイアログを表示させてそのオブジェクトを得て、Application.Sendkeysを使って操作しています。
しかし、質問者様の最初の質問にあるように、VBA実行中に意図しないダイアログボックスが出て実行が止まるのでそれを操作したいという場合、おそらくこの方法にあるようなダイアログボックスのオブジェクトの取得はできないと思います。
(もしできるのなら知識不足で申し訳ありません)
仕方がないので、他のプロセスからダイアログの出現を検出し、キーを送信することにします。
他のプロセスで動くソフトとしては、VBScript、Word、UWSCなどExcel以外でSendkeysが可能なものがいくつか考えられますが、今回はExcelの複数起動でやってみたいと思います。

考慮した点は以下のとおりです。
1.CreageObjectかNewでExcelのオブジェクトを作って別プロセスでExcelを起動し、ダイアログ操作用のブックを開く。
2.別プロセスのExcelでVBAを起動する。(マルチプロセスで動作させる方法としては、自動起動、イベント利用、Application.OnTimeの使用が考えられるが、今回はApplication.OnTimeを使用した。)
3.ダイアログ操作の方針は、ダイアログ検出と親ウィンドウのアクティブ化の両方を行った後にキー送信。ダイアログのウィンドウだけをアクティブ化してもキーを受け付けてくれないことが多い。
なお、途中にはDoEventsやSleepを適宜入れてうまく動くように調整する。
4.メインルーチンは、ダイアログのウィンドウを検出し、検出したらキーを送信する動作を繰り返す。(今回の場合ダイアログが何回出るかわからないので、検出と送信を繰り返す必要がある)
5.ダイアログ操作が不要になった場合に外部から終了できるようにしておく。(終了フラグとしてファイルのパスを指定しておいて、そのファイルが存在すると停止するようにした)
6.誤動作してアクティブ化とキー送信が連続して行われる状態になるとWindowsを手動で操作することができなくなるので、このような状態を検出して停止するようにしておく。(一定時間連続してウィンドウアクティブ化とキー送信が発生すると停止するようにした。)
7.実行時間設定機能を付加。

以上の点を考慮してコードを作成したのですが、結局解決しなかった問題があります。
ダイアログを出すExcelをアクティブ化していない場合、タスクバーが点滅するだけでダイアログが出ずに止まってしまうのです。しかしダイアログ検出のたびに親ウィンドウをアクティブ化するとVBA動作中はExcelが操作不能になってしまったので、妥協策として最初にダイアログを出すExcelをアクティブ化し、それ以降はダイアログが検出された場合のみアクティブ化することにしました。(終了まで原則人手で操作しないなら、ダイアログ検出のたびに親ウィンドウをアクティブ化する方がいいかもしれません)

キー送信用VBAのコードは字数が多くてこの回答に入りませんので、次の回答への掲載とさせていただき、先に使い方やサンプルについて述べます。
まず、次の回答に載せるコードを標準モジュールに書き込んだブックを、ファイル名 KeySender1.xlsm として保存しておきます。
次に、以下のサンプルコードを標準モジュールに書き込んだブックを、ファイル名 KSSample1.xlsm とし、KeySender1.xlsmと同じフォルダに保存します。
そして、KSSample1.xlsmのtest1を起動させれば動作するはずです。
なお、停止用のフラグファイルを同じフォルダに書き込むのでご注意ください。

ではサンプルコードを載せます。
使用法等はこのサンプルコードをご覧ください。

Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
'Sleepを使用するための宣言

'フラグファイル作成
Sub MakeFile(FFPath)
Dim fno
fno = FreeFile
Open FFPath For Output As #fno
Close #fno
End Sub

'テスト本体
Sub test1()
Dim FFP
Dim XL2 As Excel.Application
FFP = ThisWorkbook.path & "\FlagFile1" 'フラグファイルのパス付ファイル名
If Len(Dir(FFP)) > 0 Then
Kill FFP 'フラグファイルが残っていたら削除
End If
Set XL2 = New Excel.Application 'Excelを別プロセスで起動
XL2.Visible = True '起動したExcelを可視に。(可視にしなくてもよい)
XL2.Workbooks.Open ThisWorkbook.path & "\KeySender1.xlsm" 'キー送信用ブックを開く
XL2.Run "Start_SKtoD", Application.Caption, "テストメッセ", "{esc}", TimeValue("00:01:00"), FFP
'キー送信VBA"Start_SKtoD"を起動
'引数:このブックのウィンドウタイトル、ダイアログタイトル、送信キー、実行時間、フラグファイル名パス付
'タイトルは最初または最後が欠けていてもよい
DoEvents
Sleep 500
DoEvents
MsgBox "メッセージ1です", vbOKCancel, "テストメッセージ1"
'このMsgBoxはすぐに消されるはず。
MakeFile (FFP) 'フラグファイルでキー送信VBAを停止
End Sub

この回答への補足

ご回答ありがとうございます。
初めて見る内容ばかりで、非常に難しそうなので、時間をかけて読みたいと思います。

1点伺いたい事がございます。
使用しているPCが64bitなのですが、

///ご回答頂いた内容の一部///////////////////////

Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
'VBAで低CPU負荷で1ms単位で指定可能なSleepを使うための宣言
'64bit版Officeを使用している場合は要変更

////////////////////////////////////////////////

64bit版Officeの場合は要変更との事ですが、どのように変更したら良いか教えて頂けないでしょうか?

補足日時:2013/07/03 07:48
    • good
    • 0

いただいた補足を見て考えているうちに、いくつか思い違いをしていたことに気付きました。



そもそも、質問者様はShowRefで指定した関数を実行すること自体は成功していたのではないでしょうか。
それができていないと思ってサンプルコード付きの長い回答を書いてしまいました・・・

次に、小さい訂正が1つ。
StepThruですが、No.1ではTrueにすると書きましたが、Falseのままでもおそらく大丈夫です。さらに言うなら、Falseで済むならその方が速度面などで有利ですね。
Trueにすると毎回関数が呼ばれ、Falseにすると中断あるいは終了の条件になったときのみ関数が呼ばれるようです。

それから最大の勘違いですが、「値の更新」がソルバーに特有のダイアログボックスだと思ってしまい、Web検索でほとんど出ないのでどうも変だと思っていました。
しかし調べなおすとソルバーには直接の関係は何もなく、異なるブック(ファイル)の間のリンクがうまくいかない場合に出ることがあるファイル選択のダイアログボックスではありませんか。

ということで、質問者様が使用中のブックには他のブックへのリンクがあり、それが自動更新できない状態になっていると思われます。
それを解消すれば、おそらくはここに質問を出す前の状態でうまくいくのではないでしょうか。
リボン→データタブ→接続グループ→リンクの編集
などからご確認ください。

あとは、このようなダイアログのボタンを自動的に押す方法として、やや不安定ではありますが、他プロセスからSendkeysを使うといった方法があります。
これについてもご希望であれば検討いたします。

この回答への補足

ご回答をありがとうございます。
また、No.1のサンプルコードは、非常に勉強になりました。
サンプルコードは、動作する事を確認できました。

早速、リンクを確認したのですが、リンクは全くありませんでした。
(データ→編集→リンクの編集(色が変わって選択できない) 一応、接続を見たがリンクはない。

リンクは無いですが、値の更新が表示されるようです。

コードは、以下です。

[ソルバ実行の部分]
SolverSolve UserFinish:=True, ShowRef:="Dummy"

[ファンクションの部分]
Function Dummy(Reason)
If Reason = 2 Then
Dummy = True
Else
Dummy = False
End If
End Function

(1), ShowRef:="Dummy"を消す →制限時間に達すると、試行状況の表示が出る。
(2), ShowRef:="Dummy"を残す →制限時間に達すると、値の更新の表示が出る。

(1)試行状況の表示の時は、中止(T)を選択すると、問題なく終了します。その際には、値の更新は、表示されません。
コードをご覧頂き、どこか悪い所がないでしょうか?

”Sendkeys”のご紹介ありがとうございます。
ぜひ、教えていただけるとありがたいです。

補足日時:2013/06/25 22:59
    • good
    • 0

ソルバーのVBAですね。


私も使ったことがなかったので調べたり試したりいろいろやってみました。
ソルバーを実行するSolverSolve関数にはフックの仕組みがありShowRefというパラメータで指定することになっているということはすぐにわかったのですが、「ShowRefパラメータによる関数指定はうまくいかない」という趣旨の記事が多くだめなのかと思いました。
しかしやってみたら予想に反して少なくともExcel2007ではうまくいきました。

Web検索で調べていてややわかりにくかった要点としては、
1. SolverOptions関数でStepThruパラメータにTrueを指定する
2. SolverSolve関数でUserFinishパラメータにTrueを指定する
3. ShowRefのフック関数では、計算継続ならFalseを、計算終了ならTrueを返すようにする
というあたりでしょうか。

今回最も参考になったのはMSDNのSolver関連のVBA関数のところなので、以下にリンクを載せておきます。
(SolverSolve関数が重要なのは当然ですが、SolverOptions関数の説明は必見だと思います。)
http://msdn.microsoft.com/ja-jp/library/office/f …
http://msdn.microsoft.com/ja-jp/library/office/f …
http://msdn.microsoft.com/ja-jp/library/office/f …

それでもまだ安心して使えるほどには理解していないのですが、とりあえず動くサンプルを作りましたのでお試し頂ければ、と思います。

Excel2007にて新規ブックを用意し、VBEを開き、SOLVERを参照設定し、標準モジュールに以下のコードを貼り付け、Subプロシージャを実行してみてください。
うまくいけば、反復回数に大きい数字(私の環境では5以上)を入れると計算が完了して結果が保存され、小さい数字(私の環境では1~4)を入れると計算未完了で中断され結果は保存されないはずです。

Option Explicit

Private SolverCount
Private KF

Function SRF_2(Sts)
SolverCount = SolverCount + 1
Debug.Print SolverCount;
If Sts = 3 Then
Debug.Print " Stop, Sts ="; Sts;
SRF_2 = True
KF = 2
Else
SRF_2 = False
End If
End Function

Sub test_ShowRef2007_2()
Dim S
Dim MaxIteration
Set S = Sheets("Sheet1")
S.Range("B2:B3").Value = 0
S.Range("A2:A3").Value = "変化"
S.Range("B4:B6").Value = 0.1
S.Range("B7").Formula = "=(COS(B3-B5)+SIN(B2-B4-B5))/((B2-B4)^2+B6)"
S.Range("A7") = "目的"
S.Range("B8:B9") = ""
S.Range("A8") = "反復回数"
S.Range("A9") = "KeepFinal"
SolverCount = 0
KF = 1
MaxIteration = 0 + InputBox("反復回数入力", "Solver ShowRef サンプル")
SolverOk SetCell:="$B$7", MaxMinVal:=1, ValueOf:=0, ByChange:="$B$2:$B$3"
SolverOptions Iterations:=MaxIteration, StepThru:=True
SolverSolve UserFinish:=True, ShowRef:="SRF_2"
SolverFinish KeepFinal:=KF
S.Range("B8") = SolverCount
S.Range("B9") = KF
Debug.Print " Iterations: " & SolverCount & ", KeepFinal: " & KF
SolverOptions Iterations:=100, StepThru:=False
End Sub


やや余談になりますが、Excel2010でも少し調べてみました。その結果、Excel2010ではソルバーが強化されており新しいパラメータが追加されたりしているのですが、Excel2007のマクロはおおむねそのまま動くようです。
ただ、ShowRefパラメータの関数名の処理にバグがあるようで、"SR1"、"ShowRef1"などの英文字連続のあとに数字が続いてそれで終わる関数名ではうまくいきませんでした。
なので、Excel2010で同じことをやるときは関数名に配慮が必要と思われます。

何かありましたら補足ください。

参考URL:http://msdn.microsoft.com/ja-jp/library/office/f …

この回答への補足

ご回答ありがとうございます。
非常にありがたいです。

早速、要点を修正しました。
(StepThruがFalseでした。)

現状は、また「値の更新」が表示されました。

ただ、「値の更新」のタイミングが変わりました。
【修正前】 最大時間に達した時
【修正後】 ソルバが動いた直後

補足日時:2013/06/25 07:34
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています