質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • C#

    9016questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • C++

    4414questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Win32 API

    290questions

    Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

【超難問】 c# の文字をFindWindowで取得する方法が分からない

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 4,136

manbo

score 106

C# と C++(DLL)を介して「アプリケーション間で連携する方法」を勉強をしています。

●実現したいこと
C# のテキストボックスの値を、DLL で 取得し、別のアプリケーションでその値を利用したい。また、その方法は異なるPCでも同じように動作するようにしたい。

●問題としていること
C# のウィンドウクラス名がPCによって勝手に割り振られてしまい、クラス名が変わってしまう → せっかくつくったDLLが無効となってしまい異なるPCでは使えなくなる。

●疑問&分からないこと
C# のウィンドウクラス名は、あらかじめ設定できるのか? そもそもそんなことは不可能なのか? あるいは、クラス名での文字列検索ではなくこのような場合は、違うアプローチがあるのか?

●解決法として
結局は、どんな方法にしろDLLで文字列を取得できれば良いとは思っています。なので基本的にアプローチはいかなる方法でもよいと思っているのですが、いかんせんWindows API や C++ の深い知識がなくこの先に進めません。試したコードや情報はネット&書籍からのつぎはぎです。(しかし書籍が古すぎてほとんど分からず....)

DLLやメモリ空間を使いたい理由のひとつに高速かつエラーの少ない手法で実現したいと思っていてファイル書き出しなどの遅い処理をせずにできる方法はないかと思っております。

C++及びC#が初心者なため、そもそもできるかどうかも分かりません。なので、おかしなことを言ってるかもしれません。これで同じことができるまたは、解決法がご存じの方はぜひご教示いただけると嬉しいです。

【試した事&疑問 詳細】

FindWindow という Windows API で C# の文字列を取得できる事を知りました。また、これの派生で FindWindowEx という関数にて子ウィンドウの検索もできる事は分かりました、しかしC#のウィンドウクラスは、末尾が勝手に割り振られてしまい、異なるPCではウィンドウクラス名が変わってしまい、DLL で FindWindow クラスを探すコードを書いても使えなくなってしまいます。

(親ウィンドウ クラス名)
WindowsForms10.Window.8.app.xxxxxx

(子ウィンドウ クラス名)
WindowsForms10.EDIT.app.xxxxxx

そして、FindWindow は、親クラスの名前でも検索できるので、仮にC#のフォーム名が Form1 ならフォーム名で探すことはできました。しかし、子ウィンドウは、名前がないのでFindWindowExで「必ずウィンドウクラス名を指定する必要がある」と思います。ですが名前はPCによって変わってしまうという状態です。

そこで思ったのは、あらかじめ「C#側で 子ウィンドウクラス名を指定すれば!?」いいのでは?と思ったのです。しかし、そんなことは方法すらも分からず、、そもそもそんな事出来るのか?とも思います。

以下試したコードです。

C# フォーム側 (ボタンクリックでテキストボックスに1を代入)

/* testForm.cs */

/// <summary>
///  ウィンドウ情報
--------------------------------------
ハンドル   :1049732(x100484)
//ここがテキストボックスクラス名。クラス名は分かるが「Form1」のような名前がない。
//app以降は、親のウィンドウクラスを継承する。
クラス名   :WindowsForms10.EDIT.app.0.141b42a_r10_ad1   
タイトル   :
位置(左上右下):(498,562,758,632)

オーナー    :

//親ウィンドウクラス名 app 以降はPCまたはタイミング?によって勝手に変わってしまう。
親ウィンドウ  : top → bottom
 hWnd    ClassName    (Location)        Title 
 -----------------------------------------------------------------------
 722040    WindowsForms10.Window.8.app.0.141b42a_r10_ad1    (478,479,778,739)     "Form1"

子ウィンドウ

ハンドル クラス名               位置(左,上,右,下)    表示文字
-------------------------------------------------------------
/// </summary>

//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace testForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            int a = 1;

       //ボタンクリックで、テキストボックスに1を代入する
            this.textBox1.Text = a.ToString();

        }
    }
}


DLL側(親ウィンドウから、子ウィンドウを検索し、子ウィンドウの文字列を返す)

/* findwindw.cpp */

wchar_t *fWindow() {


    // メモ帳にある文字列を取得した際に、それを格納するバッファ
    char* pStrBuffer;

    // 文字列格納用バッファのサイズ
    long BufferLen;

    //ウィンドウを探す
    HWND hWnd = FindWindow(L"WindowsForms10.Window.8.app.0.141b42a_r10_ad1", L"Form1");



    if (hWnd != NULL)
    {
        //ウィンドウタイトルを取得
        char NewName[1040];
        GetWindowText(hWnd, (LPWSTR)NewName, 128);

        //子ウィンドウのウハンドルを取得
        HWND hChiWnd = FindWindowEx(hWnd, NULL, L"WindowsForms10.EDIT.app.0.141b42a_r10_ad1", L"");



        // 子ウィンドウのハンドル取得成功時
        if (hChiWnd != NULL)
        {

            // メモ帳にある文字列の列長を取得
            BufferLen = SendMessage(hChiWnd, WM_GETTEXTLENGTH, NULL, NULL);

            // 文字端末\0の分をひとつ余計目のサイズに
            BufferLen = BufferLen + 1;

            // 文字列格納用バッファの生成
            pStrBuffer = (char*)malloc(BufferLen);

            //文字列を取得して、文字列格納用バッファに設定
            SendMessage(hChiWnd, WM_GETTEXT, BufferLen, (LPARAM)pStrBuffer);



            return (LPWSTR)pStrBuffer;

        }

    }

    return 0;
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+2

>"val1" という感じでテキストは取れず、最初の1文字のみ”v”とかは取れました。
単純にC#側がUnicodeだけどC++側がANSIだからじゃないですか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/21 00:13

    同意。C#で文字列といえばUTF-16。x86アーキテクチャのようなLittle Endianの環境で例えば"val1"をchar型配列で受けると
    http://melpon.org/wandbox/permlink/Qz8CYKTnILNkrKjF
    このようにNULL文字が入ったように見えるため、Cの文字列の定義的に”v”という文字列に扱われた可能性があります。

    wchar_t型もじくはchar16_t型の配列で受け取ってみてください。

    キャンセル

  • 2016/09/30 14:09

    len_soukoさん

    ご返信遅くなりまして申し訳ございません。
    なるほどですね、Unicode と ANSI が関係しているんですね!

    キャンセル

  • 2016/09/30 14:12

    yumetodoさん

    ご返信遅くなりまして申し訳ございません。
    なんと、単純に文字コードの影響だったとは、、知識が乏しくご迷惑をおかけしました。

    キャンセル

checkベストアンサー

0

何故DLLになったのかは不明ですが…

#include <Windows.h>
#include <iostream>

int main()
{
    char str[256];

    HWND hWnd, hWndChild;

    // ウィンドウハンドルを取得
    hWnd = FindWindowEx(NULL, NULL, NULL, "Form1");
    // 子要素を取得
    hWndChild = FindWindowEx(hWnd, NULL, NULL, NULL);
    // ウィンドウキャプションを取得
    GetWindowText(hWndChild, str, 256);
    // ウィンドウキャプションを出力
    std::cout << str << std::endl;

    for (int i = 0; i < 3; i++)
    {
        // 次の要素を取得
        hWndChild = GetWindow(hWndChild, GW_HWNDNEXT);
        // ウィンドウキャプションを取得
        GetWindowText(hWndChild, str, 256);
        // ウィンドウキャプションを出力
        std::cout << str << std::endl;
    }

    return 0;
}

イメージ説明

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/18 19:16

    aglkjgggさん

    さっそくコメントいただき誠にありがとうございます。
    また事例も添付いただきありがとうございます。
    DLLにしたかった理由は、文字を取得したい先がC言語で書かれている、まったく別のアプリでしたのでDLLしかないのかなと思いまして。

    ただ、やってみたのですが、やはりできませんでした。。
    aglkjgggさんがやられているように、"val1" という感じでテキストは取れず、最初の1文字のみ”v”とかは取れました。

    しかし、これはもともとテキストボックスに値を指定していた場合で、やりたいのは、テキストボックスに後から代入された値を取り出したかったのです。なのでここの値は常に可変しており、たとえばボタンを押したとき、あるいはタイマーイベントが発生したときに、テキストボックスに入力される値を、外から取りたかったのでした。

    一応こちらにトライしたものをアップしました

    http://s1.gazo.cc/up/211491.png

    キャンセル

  • 2016/09/18 22:40 編集

    質問には
    「●実現したいこと
    C# のテキストボックスの値を、DLL で 取得し、別のアプリケーションでその値を利用したい。また、その方法は異なるPCでも同じように動作するようにしたい。」
    と、ありますが
    「DLLにしたかった理由は、文字を取得したい先がC言語で書かれている、まったく別のアプリでしたのでDLLしかないのかなと思いまして。」
    と先ほど書かれており矛盾しているように思います。
    読み取り対象のアプリが.NETアプリでない場合はクラス名は動的にならないはずですので問題ないかと思います。

    返信された情報についても対象アプリが.NET製アプリなのかWin32APIを叩いているアプリなのかはっきりわからないため的確に答えるのが難しいです。

    一応参考になりそうなリンクを載せておきます。

    C#で他アプリケーションを操作するための基礎知識 - Sanwa Systems Tech Blog
    http://tech.sanwasystem.com/entry/2015/11/25/171004

    他アプリの文字を受け取る - ×××Diary
    http://d.hatena.ne.jp/maeyan/20110120/1295457330

    自作あぷりからAPIで他のあぷりをいじるときのめも。(1/4) - ×××Diary
    http://d.hatena.ne.jp/maeyan/20091227/1261848549


    また、あまりおすすめできる方法ではありませんが、
    プロセス一覧から、読み取りさせたい対象のアプリケーションをユーザーに選択させ、
    プロセスにアタッチし、値をメモリのアドレスベースで読み取る方法もあります。
    予めかなり深い調査が必要になるため現実的ではありませんが、この場合はdll不要です。

    キャンセル

  • 2016/09/19 02:14

    aglkjgggさん

    再度、ご教示いただきありがとうございます。ご紹介いただいたURLも参考に再度頑張ってみたいと思います。

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

    キャンセル

0

C#側もC++側も質問者様が編集可能なら、共有メモリを使ってみるのはどうでしょうか?
まぁ別にディスクに書いても、よほど連続で処理しない限りは人間に感知できるような速度の差はないと思いますけれど。

【参考】

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/18 19:21

    htsignさん

    さっそくコメントいただき誠にありがとうございます。
    共有メモリ、いまいち分からず、文字を取り出したい先がC言語仕様なので、DLLをかます必要があるのかなと思い。C#ではなくC++とかじゃないといけなそうですね。

    キャンセル

  • 2016/09/18 21:31 編集

    Memory Mapped FileはWindows APIを叩くことさえできれば使えるので、言語や実装に依存しないはずです。
    .NET Framework(v4.0以降)であればフレームワークにラップされた形でAPIを使えます。
    それでなくても C だろうが C++ だろうが扱えるので、ここでなくても使える場面それなりにあると思いますよ。

    とはいえ aglkjggg さんの回答の方が今回は楽そうですね。

    キャンセル

  • 2016/09/19 02:23

    htsign

    再度コメントいただきありがとうございます。
    メモリーマップ、そうですよね。わたしもこれを最初に思いつき出来るか考えていましたが、いかんせんアンマネージの知識がなく難しそうという理由でやっておりませんでした。でもhtsignさんがおっしゃるように、メモリーマップが使えると利用場面が増えそうですよね。もっと勉強しないとだめですね。
    ご教示いただきありがとうございます。


    キャンセル

同じタグがついた質問を見る

  • C#

    9016questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • C++

    4414questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Win32 API

    290questions

    Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。