C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: VBからAccessのFormを表示


(過去ログ 177 を表示中)

[トピック内 7 記事 (1 - 7 表示)]  << 0 >>

■101547 / inTopicNo.1)  VBからAccessのFormを表示
  
□投稿者/ こうべ (4回)-(2023/03/14(Tue) 15:18:20)

分類:[.NET 全般] 

機能追加を依頼されたプログラムが不思議な構造をしております。
主要業務のみVB2008でプログラミングされていて、それ以外の業務はAccessのVBAでプログラミングされています。

このため、以下の通りにVBからAccessのFormを表示しています。

AccObj = GetObject(, "Access.Application")
Accessが起動されていないとエラーが発生します。(Err.Number = 429)

Getできた場合は、AccObj.CurrentDB.NameでMDBファイルのパスを取得します。
該当のMDBでない場合、Getしたオブジェクトをメモリ解放し、Getしなかったことにします。
Getできなかった場合、CreateObjectし、OpenCurrentDataBaseでMDBファイルを開きます。

下記でAccessのフォームを開くと、開いたフォームが背面に隠れる場合がありましたので、
SetForegroundWindowでAccess メインウィンドウを最前面にしています。

Call SetForegroundWindow(AccObj.hWndAccessApp)
AccObj.Docmd.OpenForm("Form名", 0, , , 1, 3, wArgs)

Accessのフォームを閉じると、AccessのメインフォームでVBのフォーム(呼び出し元)が隠れました
ので、下記の対策を行いました。

(1) 自分のプロセスIDを取得しておき、Accessのメインフォームからリターン後にAppActivate
  GetObjectの前     : Dim hProcess As System.Diagnostics.Process = System.Diagnostics.Process.GetCurrentProcess()
  Accessからリターン後 : Interaction.AppActivate(hProcess.Id)

(2) 自分のプログラムのメインフォームのウィンドウハンドルを取得し、SetForegroundWindowを実行

どちらの対策を行ってもAccessのメインフォームは最前面の残ったままになりました。

Me.TopMostを一時的にTrueにしてみたところ、Accessのメインフォームは背面に移動しました。
Me.TopMost = True
Me.TopMost = False

「は?」って感じで、何かきつねにつままれた様な気持ちです。

今のところ問題は起きていませんが、本当にこの対策で問題はないのでしょうか?
すごく不安です。

どなたか、ご意見をお願いします。

VB2008、Access2003を使用しています。
引用返信 編集キー/
■101548 / inTopicNo.2)  Re[1]: VBからAccessのFormを表示
□投稿者/ 大谷刑部 (229回)-(2023/03/14(Tue) 15:44:18)
No101547 (こうべ さん) に返信
> Me.TopMostを一時的にTrueにしてみたところ、Accessのメインフォームは背面に移動しました。
> Me.TopMost = True
> Me.TopMost = False
>
> 「は?」って感じで、何かきつねにつままれた様な気持ちです。

TopMostプロパティーを設定した通りの挙動になってるだけの気がしますが

https://dobon.net/vb/dotnet/form/topmost.html

そもそもどういうときにどういう挙動をする仕様にしたいんですか?
何をしたいんだか書いてある字面だけ見るとよくわかりません。
引用返信 編集キー/
■101551 / inTopicNo.3)  Re[1]: VBからAccessのFormを表示
□投稿者/ 魔界の仮面弁士 (3586回)-(2023/03/14(Tue) 17:45:08)
2023/03/14(Tue) 17:46:13 編集(投稿者)

No101547 (こうべ さん) に返信
> AccessのVBAでプログラミングされています。
> VB2008、Access2003を使用しています。
.accdb や .mde ではなく、.mdb なのですね。


> このため、以下の通りにVBからAccessのFormを表示しています。
> AccObj = GetObject(, "Access.Application")
> Accessが起動されていないとエラーが発生します。(Err.Number = 429)
Access はランタイムでの動作の場合、CreateObject で起動できなかったような…?

GetObject("C:\FOLDER\FILE.mdb") で呼び出すようにするか、
あるいは Process.Start 等であらかじめ mdb を Access で開かせておいてから、
GetObject(, "Access.Application") か Marshal.GetActiveObject("Access.Application") で拾うのが
常套手段だと思っていました。最近は違うのですかね?

2009 年当時の古い記事ですが、Microsoft Knowledge Base 147816 より。
https://bit.ly/KB147816

なお、Process.Start に渡す msaccess.exe のコマンドライン パラメーターについては下記を参照。
https://qiita.com/Q11Q/items/535dedab84fb5732e88b


> Getできた場合は、AccObj.CurrentDB.NameでMDBファイルのパスを取得します。
それだと COM インスタンスの解放漏れにつながるので、
 db = AccObj.CurrentDb
 mdbPath = db.Name
にしておいた上で、アプリ終了時に Marshal.ReleaseComObject(db) しておいた方が良い気がします。
DoCmd オブジェクトも同様ですね。

まぁ DAO の場合、DBEngine 側で Databases コレクションや Recordsets コレクションを
保持していたりしていて、Excel 以上に解放が面倒だったりするのですが……
VB 側はランチャーの役目に徹するようにし、DAO の操作を VB 側では一切行わず、VBA 側のみで
データ操作する設計であれば大丈夫かな…。


> 該当のMDBでない場合、Getしたオブジェクトをメモリ解放し、Getしなかったことにします。
> Getできなかった場合、CreateObjectし、OpenCurrentDataBaseでMDBファイルを開きます。
製品版の Access がインストールされている環境であれば CreateObject できますが、
無料の Access ランタイムしかインストールされていない環境だと、CreateObject では
呼び出せないという認識でした。


> 下記でAccessのフォームを開くと、開いたフォームが背面に隠れる場合がありましたので、
> SetForegroundWindowでAccess メインウィンドウを最前面にしています。
No101439 のスレッドで紹介した URL でも解説されていますが、外部アプリケーションのウィンドウをアクティブにするには
幾つかの条件を満たしている必要があり、単に SetForegroundWindow や AppActivate を呼ぶだけでは、必ずしも十分とは言えません。

ユーザーが何かを入力した後、システムは一定の時間(ForegroundLockTimeout)が経過するまで、強制的に
前面表示する操作が阻害されます。この設定値は既定で 200,000 ミリ秒となっているので、
先の解説にもあるよう、状況が許す場合には切り替え前にこれを解除しておく必要がありますが、
恒久的に解除しておくのと、一時的に解除して後で復元するのとどちらが良いのかはケースバイケース。


> Me.TopMostを一時的にTrueにしてみたところ、Accessのメインフォームは背面に移動しました。
Vista 以降の OS において、その方法で回避できているという報告を時折見かけますが、
その方法では、必ずしもフォーカスを奪えるわけではないのが難しいところ。場合によっては
キー入力やマウス入力のエミュレートと組み合わせて回避しているという事例もみかけますね。
https://stackoverflow.com/questions/534241/how-can-i-bring-a-window-to-the-foreground-in-vista-using-c
https://qiita.com/yaju/items/af308376f04ef2ff1325
引用返信 編集キー/
■101560 / inTopicNo.4)  Re[2]: VBからAccessのFormを表示
□投稿者/ こうべ (5回)-(2023/03/15(Wed) 11:22:15)
お恥ずかしい
私は勘違いをしていました。
Accessのメインフォームは背面に移動したことで、目的は達成したと思っていましたが、
Accessのメインフォームがアクティブのままで、目的は達成できていませんでした。

やりたかったことは、VBからAccessのFormを表示し、AccessのFormからリターン後は普通にVBのFormで操作できることです。
ただ、機能追加を依頼されたプログラムが不思議な構造をしており、Accessでメニュー画面が作られており、VBはそのメニューから起動されることがありました。(VBが単独で起動されることもあるらしい)
このことは、依頼された時には全く説明がありませんでした。

それで、CreateObjectでAccessを起動し、Accessで作成されているFormを表示させました。
そしたら、開いたフォームが背面に隠れる場合がありましたので、SetForegroundWindowでAccess メインウィンドウを最前面にしました。

その後、Accessで作成したメニュー画面からVBが起動される場合があることを知らされ、CreateObjectでAccessを起動すると同じメニュー画面が二重に表示されることになるため、Accessを起動しているかどうかをGetObjectをすることで確認し、起動されている場合は起動済のAccessでFormを表示するように変更しました。

そしたら、AccessのFormを閉じた後にAccessで作成したメニュー画面が最前面に残ったままとなったため、背面に移動しようとした次第です。
起動済のAccessでFormを表示した場合、Accessの起動はユーザーが行っているため、Accessで作成したメニュー画面は表示させたまま、VBのFormを最前面に戻し、操作できる状態にしたかった。
AppActivate、SetForegroundWindowでVBのFormを最前面に表示しようとしましたが、できなかったので、TopMostを使ってみました。
最前面に表示されたことで、てっきりうまく実行できていると思っていましたが、アクテイブだったのはAccessでした。



引用返信 編集キー/
■101561 / inTopicNo.5)  Re[3]: VBからAccessのFormを表示
□投稿者/ 大谷刑部 (230回)-(2023/03/15(Wed) 12:48:22)
No101560 (こうべ さん) に返信
> お恥ずかしい
> 私は勘違いをしていました。
> Accessのメインフォームは背面に移動したことで、目的は達成したと思っていましたが、
> Accessのメインフォームがアクティブのままで、目的は達成できていませんでした。
>
> やりたかったことは、VBからAccessのFormを表示し、AccessのFormからリターン後は普通にVBのFormで操作できることです。
> ただ、機能追加を依頼されたプログラムが不思議な構造をしており、Accessでメニュー画面が作られており、VBはそのメニューから起動されることがありました。(VBが単独で起動されることもあるらしい)
> このことは、依頼された時には全く説明がありませんでした。

依頼主が完全にお客様、いわゆるエンドユーザーなのか、それとも社内システムとかの担当者なのかによって事情は異なるとは思いますが、
失礼ながら、そのような言い方、書き方をするとあなたが言い訳をしているように聞こえてしまいます。
説明がないからわからなかったではなく、先方にまずはじめにそういう特殊な仕様がないか確認しないとダメでしょう。


> それで、CreateObjectでAccessを起動し、Accessで作成されているFormを表示させました。
> そしたら、開いたフォームが背面に隠れる場合がありましたので、SetForegroundWindowでAccess メインウィンドウを最前面にしました。
>
> その後、Accessで作成したメニュー画面からVBが起動される場合があることを知らされ、CreateObjectでAccessを起動すると同じメニュー画面が二重に表示されることになるため、Accessを起動しているかどうかをGetObjectをすることで確認し、起動されている場合は起動済のAccessでFormを表示するように変更しました。
>
> そしたら、AccessのFormを閉じた後にAccessで作成したメニュー画面が最前面に残ったままとなったため、背面に移動しようとした次第です。
> 起動済のAccessでFormを表示した場合、Accessの起動はユーザーが行っているため、Accessで作成したメニュー画面は表示させたまま、VBのFormを最前面に戻し、操作できる状態にしたかった。
> AppActivate、SetForegroundWindowでVBのFormを最前面に表示しようとしましたが、できなかったので、TopMostを使ってみました。
> 最前面に表示されたことで、てっきりうまく実行できていると思っていましたが、アクテイブだったのはAccessでした。

そもそも.NetFramework(ですよね?VB2008でさすがに.Net6以降はあり得ないと思うので)で作成した画面と、Accessという別のアプリの一部品であるFormを同列に考える方が無茶な気もします。
Accessと.Netで作った画面(というよりExe)が同列が同列なので。
異なるアプリケーションでどっちも主みたいな仕様がものすごい不自然に感じるので、まずは依頼主とどっちを「元」にするかを相談した方がいいのでは?
一番最悪なのは、この画面遷移の場合は.Net側で制御して、この場合はAccess側で制御してみたいになるのがシステムの仕様をぐちゃぐちゃにする元凶になると思います。

ちなみにAccess側のFormはオートオープンですか?
引用返信 編集キー/
■101563 / inTopicNo.6)  Re[3]: VBからAccessのFormを表示
□投稿者/ 魔界の仮面弁士 (3589回)-(2023/03/15(Wed) 13:43:12)
No101560 (こうべ さん) に返信
> やりたかったことは、VBからAccessのFormを表示し、AccessのFormからリターン後は普通にVBのFormで操作できることです。

Access Form が閉じた段階で、Access 本体を終了あるいは最小化する処理を仕込んでみては如何でしょうか?
そうすれば、直前にアクティブだったアプリケーションがアクティブになる気がします。

Access 本体の終了は Application.Quit メソッドを呼び出せばよいはず。
最小化なら、CloseWindow API に Application.hWndAccessApp プロパティを渡します。
(これらの呼び出しを、VBA 側で行うのか VB 側で行うのかは要検討)


もしも Access 本体はそのままで、VB の Form をアクティブにしたいというのであれば、
VB 側を再アクティブ化するための処理を、VBA 側か VB 側かのいずれかに盛り込む必要がありそうな予感。


(案1) 直前までアクティブであった Access 側の VBA で、
 呼び出し元の VB Form をアクティブにする処理を記述する。
 (VBA 側に API コードを盛り込んでいく必要がある)


(案2) 起動した Access 側の Access.Form オブジェクトのインスタンスを
 VB 側に保持させておき(ActiveForm プロパティなどで得られると思います)、
 Access 側で Form が閉じられたことを、VB 側が Access.Form のイベント
 (Close イベントあるいは Unload イベント)を捉えて、VB 側が自身の Form を
 アクティブにするように記述する。


> AppActivate、SetForegroundWindowでVBのFormを最前面に表示しようとしましたが、できなかったので、TopMostを使ってみました。
> 最前面に表示されたことで、てっきりうまく実行できていると思っていましたが、アクテイブだったのはAccessでした。
SetForegroundWindow する前に、アクティブだった側(Access)が、非アクティブな相手(VB)に対して AttachThreadInput を呼び出してみてはどうでしょう。
引用返信 編集キー/
■101565 / inTopicNo.7)  Re[4]: VBからAccessのFormを表示
□投稿者/ 大谷刑部 (231回)-(2023/03/15(Wed) 14:42:32)
No101563 (魔界の仮面弁士 さん) に返信
> ■No101560 (こうべ さん) に返信
>>やりたかったことは、VBからAccessのFormを表示し、AccessのFormからリターン後は普通にVBのFormで操作できることです。
>
> Access Form が閉じた段階で、Access 本体を終了あるいは最小化する処理を仕込んでみては如何でしょうか?
> そうすれば、直前にアクティブだったアプリケーションがアクティブになる気がします。

基本的には弁さんのこの案に賛成です。
最少化でも、ShowWindowAsyncとかで元に戻せるので、ある程度は.Net側で制御可能ですしね。
Accessの起動時に重ための処理をしてる場合なんかは最小化の方にした方がむしろいいかもしれません。
プロセスさえ捕捉できれば、起動してるアプリはそのまま利用した方が再表示するのに処理が軽いでしょうし。

どっちかというとどちらからも双方向に起動できる仕様になってる方が問題のような気がしていて、どっちが主なのかはっきりして、
プロセス制御は片方に固めた方がシステムが煩雑化せずトラブルは減るとは思います。
もちろんどうするかは質問者さんの自由ですが。
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -