VBからのIDをVCへ渡すの方法

[戻る]
GG 2006/10/11(水) 11:50:32
お世話になっています、
 
VCからVBの*.exeを起動しています。
ShellExecuteEx(…);

問題というのは、
起動されたVBの*.exeにListBoxがあります、
このListBoxに選択されたIDをVC側に渡したいのです、
これが可能でしょうか。
もし可能とすれば、どういう関数を使えばいいでしょうか。

環境:.Net MFC使用、 Win2000, VB6.0

よろしくお願いします。

YuO [E-Mail] 2006/10/11(水) 12:14:53
VCプログラムにメッセージを送ってもらうなり(PostMessage, SendMessage, 
PostThreadMessage, etc.),
ソケット使うなり,パイプ使うなり,共有メモリ使うなり,方法はいくつもあります。

PATIO 2006/10/11(水) 13:23:55
プロセス間通信と言う奴を勉強されてた方が良いと思います。
開発言語がVBだろうが、VCだろうが、実行ファイルが別になっているプログラム間で
通信するにはプロセス間通信の知識が必要です。
まあ、Windowsの場合はVCのプログラムがウインドウを持っているなら
ウインドウメッセージを使った通知が一番楽でしょうね。

方法は色々ありますから、YuOさんの意見を参考にされてはどうでしょう。

PATIO 2006/10/11(水) 13:25:41
追伸:
実行ファイルが同じであっても複数回起動して別々に動作していれば、
その間でもプロセス間通信は必要になりますね。
この辺はプロセスと言う概念をきちんと理解した方が良いかもしれません。

GG 2006/10/11(水) 16:19:04
YuOさん、PATIOさん
ご返事ありがとう.

試しにやってみたのですが、
既に起動してアプリを終了させるということですが、

   hwindow = FindWindow(vbNullString, "電卓")
 If hwindow = 0 Then MsgBox "エラー"
  MsgBox hwindow
    Call PostMessage(hwindow, WM_QUIT, 0, 0)
成功したのですが、

次に自分のアプリを終了させるとうまく行かないです。
(終了しない)
 hwindow = FindWindow(vbNullString, "アプリ")
 If hwindow = 0 Then MsgBox "エラー"
  MsgBox hwindow
    Call PostMessage(hwindow, WM_QUIT, 0, 0) //ここまでOK

結果的には終了しない。

よろしくお願いします。

επιστημη 2006/10/11(水) 16:26:04
"自分のアプリ"がWM_QUITメッセージを受理したとき何をしていますか?

おと 2006/10/11(水) 17:17:42
>VCからVBの*.exeを起動しています。
>ShellExecuteEx(…);

その前にVCからVCをやってみるとわかることがあります

GG 2006/10/12(木) 08:24:21
επιστημη さん、おとさん
ご返事ありがとう。

>"自分のアプリ"がWM_QUITメッセージを受理したとき何をしていますか?
MFCを使っていますが、アプリ終了ということは自動的にやっているのですが、
特に、弄っていないです。
MFCの場合、ID_APP_EXITしかありません。


>その前にVCからVCをやってみるとわかることがあります
VC=>VBの*.exe ができました、ちなみに、VCのIDがVBへ送って、
VB側に受け取るもできました。

VC=>VCの*.exe(起動させる)やったことがないので、これからやってみます。
ちょっと聞きたいのは、VC(起動させる)側にIDなど受け取る関数があるのでしょうか。
ようするに、VBの場合、Command()ような関数。

よろしくお願いします。

PATIO 2006/10/12(木) 12:27:00
VB側のIDを受け取ると言う意味でいっているならそもそも無理な話です。
プロセスが違うので直接受け取るような事はできないと思います。
こういう部分はプロセスと言う概念がわかっていないと多分ピンとこないと思います。
なので、「プロセスと言う概念をきちんと理解した方が良いかもしれません。」と
書いています。

別のプロセスに対して何かやってもらおうとしたらそれを伝える為に
プロセス間通信を行う必要があります。
ウインドウメッセージはあくまでも一例なのでそれで事足りるような事を
しようとしているのかどうかが分からない以上はこれ以上はアドバイスができません。
具体的にどういう事がしたいのかを説明してください。
VCのプログラムはVB側から具体的にどういう情報をもらう事ができれば良いのか?

あと、VCの掲示板でVBの関数を例に出してもVBの知識がない人間には伝わらないので
別の表現方法を考えた方が良いです。まあ、VBを知っている人間であれば伝わると
思うのですが。
私の場合はVBは触った程度しか使った事が無いのでピンと来ない方の人間です。

さやぴ 2006/10/13(金) 10:01:49
共有メモリを使ってVBのリストボックスのインデックスを取得する方法を試してみまし
た。なお、エラー処理等は入っていません。

また、プロセス間の同期をどうするかはよく分かりません。
データの受け渡しより、こちらの方が大変なような気がしますが・・・。

'-----------------------------------------------------------------------------
'VC++側の処理
'-----------------------------------------------------------------------------

//データメンバ(ヘッダー)に共有メモリハンドルを宣言
class C*****Dlg : public CDialog
{
// インプリメンテーション
protected:
    HANDLE m_hMapping; // 共有メモリハンドル


BOOL C*****Dlg::OnInitDialog()
{
    // TODO: 特別な初期化を行う時はこの場所に追加してください。

    // 共有メモリを作成
    m_hMapping = ::CreateFileMapping( INVALID_HANDLE_VALUE,  NULL,
                  PAGE_READWRITE, 0, 1024, "map_name");

    // マッピングする
    long* lpBuf = (long *)::MapViewOfFile( m_hMapping, FILE_MAP_ALL_ACCESS,
                   0, 0, 0);

    // 初期化
    ::ZeroMemory( lpBuf, 1024);

    // マッピングを解除
    ::UnmapViewOfFile( lpBuf);

    return TRUE;  // TRUE を返すとコントロールに設定したフォーカスは失われませ
ん。
}

void C*****Dlg::OnButton1() 
{
    // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください

    // 共有メモリをオープン
    long* lpBuf = (long *)::MapViewOfFile( m_hMapping, FILE_MAP_READ, 0, 0, 0);

    // 確認用(エディットボックスへ表示)
    CEdit* myED = (CEdit *)GetDlgItem(IDC_EDIT1);

    CString strBuf;
    strBuf.Format("%ld",*lpBuf);
    myED->SetWindowText(strBuf);

    // マッピングを解除
    ::UnmapViewOfFile( lpBuf);
}


'-----------------------------------------------------------------------------
'VB側の処理
'-----------------------------------------------------------------------------

Private hFileMap As Long
Private lpFileMap As Long

Private Const FILE_MAP_WRITE = &H2

Private Declare Function MapViewOfFile Lib "kernel32" _
    (ByVal hFileMappingObject As Long, _
     ByVal dwDesiredAccess As Long, _
     ByVal dwFileOffsetHigh As Long, _
     ByVal dwFileOffsetLow As Long, _
     ByVal dwNumberOfBytesToMap As Long _
     ) As Long

Private Declare Function OpenFileMapping _
    Lib "kernel32" Alias "OpenFileMappingA" _
    (ByVal dwDesiredAccess As Long, _
     ByVal bInheritHandle As Long, _
     ByVal lpName As String _
     ) As Long

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    (ByRef Destination As Any, _
     ByRef Source As Any, _
     ByVal Length As Long)

Private Declare Function FlushViewOfFile Lib "kernel32" _
    (ByRef lpBaseAddress As Any, _
     ByVal dwNumberOfBytesToFlush As Long _
     ) As Long


'***********************************
'共有メモリへ書き込み
'***********************************
Private Sub List1_Click()
    
    Dim id As Long
    id = List1.ListIndex
    
    hFileMap = OpenFileMapping(FILE_MAP_WRITE, 0, "map_name")
    lpFileMap = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0&, 0&, 0&)
        
    CopyMemory ByVal (lpFileMap), id, Len(id)
    FlushViewOfFile ByVal lpFileMap, Len(id)

End Sub

GG 2006/10/16(月) 19:03:01
[[解決]]
PATIO さん、さやぴ さん
ご返事ありがとう。

返事が遅かったすみませんでした。

さやぴ さんが書いたコードを確認しました。
問題がなくListのIndexが取得できました。


>また、プロセス間の同期をどうするかはよく分かりません。
その辺私もわかりません、これからやってみようと思っています。

>データの受け渡しより、こちらの方が大変なような気がしますが・・・。
確かに。


最後、さやぴ さんのすばらしいコードをいただいて、
ほんとに、ありがとうございました。
これから、VCとVBを融合にしていく、頑張ります。

Atata!! [HomePage] 2006/10/16(月) 22:37:49
始めて書き込ませていただきます。Atata!!と申します。

既に解決済みですが、VB側のソースが修正可能であるなら、
オートメーションを使用する方法が簡単で良いかと思います。
めんどくさいのでそのままソースを貼り付けさせて貰いますが、以下のようになります。

'-----------------------------------------------------------------------------
'VB側の処理
'Form上にList1って名前のリストボックスを貼り付けて下さい。
'-----------------------------------------------------------------------------
Option Explicit

Private Const ACTIVEOBJECT_STRONG As Long = 0
Private Const ACTIVEOBJECT_WEAK As Long = 1

Private Const APPLICATION_CLSID As String = "{3eca5a91-5803-4f6b-95a7-
81a0f64c0b08}"

Private Type Guid
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

Private Declare Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As String, 
ByRef pclsid As Guid) As Long

Private Declare Function RegisterActiveObject Lib "OLEAUT32.DLL" (ByVal punk As 
Object, ByRef rclsid As Guid, ByVal dwFlags As Long, ByRef pdwRegister As Long) 
As Long

Private Declare Function RevokeActiveObject Lib "OLEAUT32.DLL" (ByVal 
dwRegister As Long, ByVal pvReserved As Long) As Long

Private register_id As Long

Private Sub Form_Load()
Dim hr As Long
Dim clsid As Guid

    Call CLSIDFromString(StrConv(APPLICATION_CLSID, vbUnicode), clsid)

    hr = RegisterActiveObject(Me.List1, clsid, ACTIVEOBJECT_STRONG, register_id)
    If hr < 0 Then
        MsgBox "登録失敗"
    End If

End Sub

Private Sub Form_Unload(Cancel As Integer)

    Call RevokeActiveObject(register_id, 0)

End Sub


//-----------------------------------------------------------------------------
//VC++側の処理
//-----------------------------------------------------------------------------
#include <ole2.h>

wchar_t APPLICATION_CLSID[] = L"{3eca5a91-5803-4f6b-95a7-81a0f64c0b08}";

int main(int argc, char* argv[])
{
    CLSID clsid;
    HRESULT hr;
    IUnknown* unknown;
    IDispatch* dispatch;
    LPOLESTR propertynames[] = {L"ListIndex"};
    DISPID dispid;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    VARIANT result;

    OleInitialize(NULL);

    CLSIDFromString(APPLICATION_CLSID, &clsid);

    hr = GetActiveObject(clsid, NULL, &unknown);
    if (SUCCEEDED(hr))
    {
        hr = unknown->QueryInterface(&dispatch);
        if (SUCCEEDED(hr))
        {
            hr = dispatch->GetIDsOfNames(GUID_NULL, propertynames, 1, 0, 
&dispid);

            if (SUCCEEDED(hr))
            {
                VariantInit(&result);

                hr = dispatch->Invoke(dispid, GUID_NULL, 0, 
DISPATCH_PROPERTYGET, &dispparams, &result, NULL, NULL);
            }

            if (SUCCEEDED(hr))
            {
                VariantClear(&result);
            }

            dispatch->Release();
        }

        unknown->Release();
    }

    OleUninitialize();

    return 0;
}



この手法はレジストリを使用しなくても良いため私が好んで使用する手法ですが、
本来はVB自身の機能でオートメーションオブジェクトを公開する方が良いと思います。

問題となる同期はCOMのプロセス間通信部分が勝手にやってくれます。
逆にVB側で重たい処理をやってるとVC++から呼び出した時に固まりますが・・・。
まぁ回避方法はいろいろあります。

Atata!! [HomePage] 2006/10/16(月) 22:40:00
投稿してから気づきましたがVB側の改行されてるところは、
コンパイルエラーが出ないように適当に整形してください。

Atata!! [HomePage] 2006/10/16(月) 22:45:44
VC++側のどこで値が取れてるか示す部分を忘れてましたが、

                hr = dispatch->Invoke(dispid, GUID_NULL, 0, 
DISPATCH_PROPERTYGET, &dispparams, &result, NULL, NULL);

の後で result に値が帰ってきます。デバッガで中身を確認してみて下さい。


久々によそのサイトに投稿するんで、中々抜けが多いもんですわ。

GG 2006/10/19(木) 10:33:21
Atata!! さん
ご返事ありがとう.

サンプルありがとう。
確認したのですが、ListIDを取得できました。

コードを見たら、びっくりしました、
自分の知識の範囲外でした。

Atata!! さんの[HomePage]をざっと見ました、
今書いたコードはOLEということでしょうか。
(汗)

よろしくお願いします。

PATIO 2006/10/19(木) 12:00:15
COMについて勉強されるといいかと思います。
VB側に手を入れる事まで考えたらCOMが正解かもしれません。

GG 2006/10/20(金) 11:11:47
PATIOさん
ご返事ありがとう.

>COMについて勉強されるといいかと思います。
そうですね、これから勉強しようと思っています。

ありがとうございました。

古いスレッドには追加発言できません。