VC++/CLIにてEnumWindows/EnumWindowsProcを実行するには

[戻る]
初心者 2014/03/07(金) 00:09:34
いつもこの「ラウンジ」利用させて戴いて、毎回親切なアドバイスを頂いている者で
す。

環境:VC++2008EE、Windows-7,Vista、VC++/CLI

タイトルの通り 上記の環境にてEnumWindows/EnumWindowsProcを実行する方法がわかり
ません。(CALLBACKの設定(__stdCallと__clrCall)がわかりません。)
以下のようにしているのですが・・・、

private delegate bool EnumWindowsCB(IntPtr hWnd, IntPtr lParam);
ref class EnmWin{
public:
 bool Func(IntPtr hWnd, IntPtr lp) {
  EnumWindowsCB ^callback = gcnew EnumWindowsCB(this,EnmWin::EnumWindowProc);
  EnumWindows(callback, 0);
  return true;
 }
 bool EnumWindowsProc(IntPtr hWnd, IntPtr lp) {
  return true;
 }
static bool EnumWindows(EnumWindowsCB ^func, int lp);
};

static BOOL  EnumWindowsProc(HWND hWnd, LPARAM lParam){
・・・
}

private: System::Void button1_Click(...){
 EnmWin ^ew = gcnew EnmWin;
 EnumWindowsCB ^ StaticInst = gcnew EnumWindowsCB(ew, &EnmWin::Func);
 EnumWindows( StaticInst, 0);・・・ここで以下のエラー発生(コンパイルのとき)
...
}

エラーメッセージは
C2664: 'EnumWindows' : 1 番目の引数を 'test3::EnumWindowsCB ^' か
ら 'WNDENUMPROC' に変換できません。

キャストしてもキャストできません・・というエラーが発生。

SDKを使うと何の問題もなく出来るようですが・・・
C#のものはインターネットで盛んに出てくるのですが・・・

どこがおかしいのか教えてください。
よろしくお願いいたします。

みい 2014/03/07(金) 21:33:28
手元に資料がないのでうろ覚えですが…
delegateの前に
[UnmanagedFunctionPointer(CallingConvention::XXXX)]
(XXXXは呼出規約名)
は入れてますか?

επιστημη 2014/03/07(金) 22:01:40
Form1 に ボタン(button1)とリストボックス(listBox1)を貼り、
ボタン押したらEnumWindowsで得られたhwndそれぞれのWindowTextを取得して
リストボックスに列挙する。

::: Form1.h
#pragma once
#include <Windows.h> // 追加

namespace XXXX {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;

    /// <summary>
    /// Form1 の概要
    /// </summary>
    public ref class Form1 : public System::Windows::Forms::Form
    {
...
        // 追加
    public: BOOL EnumWinProc(HWND wnd, LPARAM param);
        // button1のイベントハンドラ
    private: System::Void button1_Click(System::Object^  sender, System::Eve
ntArgs^  e);
             
};
}

::: Form1.cpp
#include "stdafx.h"
#include "Form1.h"
#include <msclr/gcroot.h>

struct holder {
    msclr::gcroot<XXXX::Form1^> form;
};

BOOL CALLBACK InternalCallback(HWND wnd, LPARAM param) { 
  holder* ph = (holder*)(param);
  return ph->form->EnumWinProc(wnd,0);
}

namespace XXXX {

    System::Void Form1::button1_Click(System::Object^  sender, System::Event
Args^  e) {
        holder h; h.form = this;
        EnumWindows(&InternalCallback,(LPARAM)(&h));
    }

    BOOL Form1::EnumWinProc(HWND wnd, LPARAM param) {
        wchar_t buffer[256];
        GetWindowTextW(wnd, buffer, 256);
        if ( buffer[0] != L'\0' ) {
            System::String^ title = gcnew System::String(buffer);
            listBox1->Items->Add(title);
        }
        return TRUE; 
    }

}

初心者 2014/03/08(土) 05:30:31
みい さん。
[UnmanagedFunctionPointer(CallingConvention::XXXX)]のXXXXには、Cdecl、Winapi、
など4種類ありますが、すべてだめでした。
ご示唆ありがとうございます。(礼!)

επιστημηさん。親切に本当に感謝いたします(礼!)
マネージクラスとアンマネージクラスですが、私は何も分かっていませんでした。
教えていただいたのはアンマネージ内でマネージを使うということでいいのでしょう
か。(逆ですか?????)
教えていただいたコードを実行すると、リンク時に以下のエラーがそれぞれ、計4つ発
生しました。

項目
未解決のトークン
未解決の外部シンボル

対象関数
GetWindowTextW(・・・)
EnumWindows(・・・)

内容
extern "C" int __stdcall・・が・・_clrcall・・にて参照されました

/clr、/MDdにてコンパイルしていますがそこらへんでしょうか?

επιστημη 2014/03/08(土) 07:20:48
user32.lib をリンクしてやらにゃならんでしょう。

初心者 2014/03/08(土) 15:08:02
[[解決]]
επιστημηさん。 いつもありがとうございます。


#pragma comment(lib,"shell32.lib")
を付けていて、user32.lib と勘違いしてました。うまくできました。ありがとうござい
ました。

・・・・・ということは、これの問題で、すべてマネージクラス内で・・ができなかっ
た???(CALLBACKをマネージから読み込む)のでしょうか。試してみます。

でも、CLIの場合かんたんなものはすべてOOO.hの中でかけてしまうので、今回の
struct holder {
    msclr::gcroot<XXXX::Form1^> form;
};

のコードは初めて知りました。大変便利です。
ありがとうございました。(御礼!)

επιστημη 2014/03/08(土) 16:07:24
[[解決]]
ツブシが効くよう、templateで書き換えた版:

#include "stdafx.h"
#include "Form1.h"
#include <msclr/gcroot.h>

template<typename Owner>
struct holder {
    msclr::gcroot<Owner> owner;
    LPARAM param;
    holder(Owner o, LPARAM p) : owner(o), param(p) {}
    operator LPARAM() const { return reinterpret_cast<LPARAM>(this); }
};

BOOL CALLBACK InternalCallback(HWND wnd, LPARAM param) { 
  holder<XXXX::Form1^>* ph = reinterpret_cast<holder<XXXX::Form1^>*>(param);
  return ph->owner->EnumWinProc(wnd,ph->param);
}

namespace XXXX {

    System::Void Form1::button1_Click(System::Object^  sender, System::Event
Args^  e) {
        holder<XXXX::Form1^> h(this, 0);
        EnumWindows(&InternalCallback,h);
    }

    BOOL Form1::EnumWinProc(HWND wnd, LPARAM param) {
        wchar_t buffer[256];
        GetWindowTextW(wnd, buffer, 256);
        if ( buffer[0] != L'\0' ) {
            System::String^ title = gcnew System::String(buffer);
            listBox1->Items->Add(title);
        }
        return TRUE; 
    }

}

初心者 2014/03/08(土) 18:19:12
επιστημηさん、本当にありがとうございます。

まずはお礼をと思い、返信しました。

「名前」の通り本当の初心者ですので、「template」も解っていません。
これから教えていただいたコードを【解読】していきます。

わざわざ、ご親切に2つのご示唆ありがとうございます。(御礼!!)

ところで、できたらもうひとつ教えてください。
(本当に、「できたら」で結構です。)
例えば、「Form1.h」の中にnamespaceを複数作れるでしょうか。

というのは、先ほどの私の『解決』版に書いたように
{???(CALLBACKをマネージから読み込む)のでしょうか。試してみます。}
の実行を始めたばかりで、もしかして、もうひとつnamespaceを作って、そこに
delegate版を置けばできるかな・・・と思って始めたばかりです。

ですから、私もまだ試行錯誤していない段階なので、失礼な質問です。(お詫び!)

しかし、本当にありがとうございました。

επιστημη 2014/03/08(土) 20:06:10
namespace XXXX {
    ...
}

namespace YYYY {
    ...
}

でも

namespace XXXX {
    ...
    namespace YYYY {
        ...
    }
}

でもお好きに。

初心者 2014/03/09(日) 21:01:06
επιστημηさん

ありがとうございました。
なんかすべてエラー(構文エラー、型指定子がありません)がでて
うまくいきませんでした。

やっぱり、C言語の基本にのっとり、cppにて記述します。

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

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