web-dev-qa-db-ja.com

user32.dllのFindWindowExがdllimportを使用してゼロのハンドルとエラーコード127を返す

別のWindowsアプリケーションをプログラムで処理する必要があります。Googleを検索すると、DLLImport属性を使用してWindows計算機を処理し、user32.dll関数をC#のマネージド関数にインポートするサンプルが見つかりました。

アプリケーションが実行中です。メインウィンドウのハンドル、つまり電卓自体を取得していますが、その後のコードは機能しません。 FindWindowExメソッドは、ボタンやテキストボックスなどの電卓の子のハンドルを返しません。

DLLImportでSetLastError = Trueを使用してみましたが、「プロシージャが見つかりません」というエラーコード127が表示されます。

これは私がサンプルアプリケーションを入手した場所からのリンクです:

http://www.codeproject.com/script/Articles/ArticleVersion.aspx?aid=14519&av=345

誰かがそれを解決する方法を知っているなら助けてください。

更新:DLLImportは次のとおりです。

[DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className,  string  windowTitle);

機能していないコードは次のとおりです。

hwnd=FindWindow(null,"Calculator"); // This is working, I am getting handle of Calculator

// The following is not working, I am getting hwndChild=0 and err = 127
hwndChild = FindWindowEx((IntPtr)hwnd,IntPtr.Zero,"Button","1");

                Int32 err = Marshal.GetLastWin32Error();
11
teenup

実行しようとしているコードは、個々のボタンのキャプションに依存してボタンを識別しています。たとえば、次のコードを使用して、「1」ボタンへのハンドルを取得します。

hwndChild = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "1");

これは、ウィンドウクラスの名前に「ボタン」を指定し、ウィンドウの名前に「1」を指定します(ボタンの場合、これはボタン自体に表示されるキャプションテキストと同じです)。

このコードはWindows XP(および以前のバージョン)で正常に機能し、電卓のボタンはテキストキャプションで識別されました。「1」ボタンには「1」というウィンドウ名が付いていたため、「1ボタンのキャプションとして「」が表示されました。

しかし、Windows 7では状況が変わったようです(おそらくVistaでも同様ですが、そのようなシステムにアクセスできないため、確認できません)。 Spy ++を使用して計算機ウィンドウを調査すると、「1」ボタンに「1」というウィンドウ名がなくなっていることが確認されます。実際、ウィンドウ名はまったくありません。キャプションはNULLです。おそらく、電卓の新しいデザインではボタンをカスタム描画する必要があったため、キャプションはどのボタンがどの機能に対応するかを示す必要がなくなりました。カスタムペイントルーチンは、必要なキャプションを描画します。

指定したウィンドウテキストではボタンが見つからないため、ウィンドウハンドルの値0(NULL)が返されます。

FindWindowEx関数のドキュメント は、NULLパラメータにlpszWindowを指定できることを示していますが、これはもちろん一致しますall指定されたクラスのウィンドウ。電卓アプリにはたくさんのボタンがあるので、おそらくこの場合は望んでいないでしょう。

良い回避策がわかりません。電卓はこのように「自動化」されるようには設計されておらず、Microsoftは内部の仕組みを変更しないことを保証していません。これは、このアプローチを使用して他のアプリケーションのウィンドウを混乱させるリスクです。


EDIT:リンクしたコードは、以前のバージョンのWindowsでも、かなり深刻な別の方法でも間違っています。 hwnd変数をint型ではなく、IntPtr型として宣言します。ウィンドウハンドルはポインターなので、常にIntPtr型として格納する必要があります。これにより、赤いフラグを送信するはずのFindWindowEx関数呼び出しの醜いキャストも修正されます。

また、SendMessageの宣言を修正して、最初のパラメーターのタイプがIntPtrになるようにする必要もあります。

コードは次のように記述されているはずです。

IntPtr hwnd = IntPtr.Zero;
IntPtr hwndChild = IntPtr.Zero;

//Get a handle for the Calculator Application main window
hwnd = FindWindow(null, "Calculator");
if(hwnd == IntPtr.Zero)
{
    if(MessageBox.Show("Couldn't find the calculator" + 
                       " application. Do you want to start it?", 
                       "TestWinAPI", 
                       MessageBoxButtons.YesNo) == DialogResult.Yes)
    {
        System.Diagnostics.Process.Start("Calc");
    }
}
else
{
    //Get a handle for the "1" button
    hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "1");

    //send BN_CLICKED message
    SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero);

    //Get a handle for the "+" button
    hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "+");

    //send BN_CLICKED message
    SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero);

    //Get a handle for the "2" button
    hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "2");

    //send BN_CLICKED message
    SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero);

    //Get a handle for the "=" button
    hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "Button", "=");

    //send BN_CLICKED message
    SendMessage(hwndChild, BN_CLICKED, 0, IntPtr.Zero);
}
13
Cody Gray

次のコードは、ClassicテーマのWindows 7の電卓で正常に機能します(BasicまたはAeroテーマでは機能しません)。

==================================================

IntPtr hwndFrame = FindWindowEx(hwnd, IntPtr.Zero, "CalcFrame", null); 
IntPtr hwndDialog = FindWindowEx(hwndFrame, IntPtr.Zero, "#32770", null); 
IntPtr hwndDialog2 = FindWindowEx(hwndFrame, (IntPtr)hwndDialog, "#32770", null);


IntPtr hwndThree = FindWindowEx(hwndDialog2, IntPtr.Zero, "Button", "3"); 
SendMessage((int)hwndThree, BN_CLICKED, 0, IntPtr.Zero);

IntPtr hwndPlus = FindWindowEx(hwndDialog2, IntPtr.Zero, "Button", "+");
SendMessage((int)hwndPlus, BN_CLICKED, 0, IntPtr.Zero);

IntPtr hwndOne = FindWindowEx((IntPtr)hwndDialog2, IntPtr.Zero, "Button", "1");
SendMessage((int)hwndOne, BN_CLICKED, 0, IntPtr.Zero);

IntPtr hwndEqual = FindWindowEx(hwndDialog2, IntPtr.Zero, "Button", "=");
SendMessage((int)hwndEqual, BN_CLICKED, 0, IntPtr.Zero);
2
Yanzhi

これをWin7 Proで再現できました。問題は、ボタンのラベルがキャプションとしてではなく、電卓のテーマを介して描かれていることです。テーマサービスの実行中に電卓を起動すると、キャプションのないボタンが表示されます。

適切なボタンのキャプションを取得するには、以下を行う必要があります。

  1. テーマサービスを停止します(管理者特権のコマンドプロンプトからnet stop themesを実行するか、サービス管理ツールを使用します)。
  2. 電卓を起動します。

テーマサービスを停止するときに電卓を実行している場合、そのボタンがすべて空白になっていることがわかります。

2
Gabe