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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

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

Win32 API

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

C++

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

Q&A

解決済

3回答

13357閲覧

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

manbo

総合スコア114

C#

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

Win32 API

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

C++

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

0グッド

1クリップ

投稿2016/09/17 17:16

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; }

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

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

投稿2016/09/20 04:16

len_souko

総合スコア1348

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yumetodo

2016/09/20 15:13

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

2016/09/30 05:09

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

2016/09/30 05:12

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

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 01:23

編集2016/09/18 01:25
aglkjggg

総合スコア769

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

manbo

2016/09/18 10:16

aglkjgggさん さっそくコメントいただき誠にありがとうございます。 また事例も添付いただきありがとうございます。 DLLにしたかった理由は、文字を取得したい先がC言語で書かれている、まったく別のアプリでしたのでDLLしかないのかなと思いまして。 ただ、やってみたのですが、やはりできませんでした。。 aglkjgggさんがやられているように、"val1" という感じでテキストは取れず、最初の1文字のみ”v”とかは取れました。 しかし、これはもともとテキストボックスに値を指定していた場合で、やりたいのは、テキストボックスに後から代入された値を取り出したかったのです。なのでここの値は常に可変しており、たとえばボタンを押したとき、あるいはタイマーイベントが発生したときに、テキストボックスに入力される値を、外から取りたかったのでした。 一応こちらにトライしたものをアップしました http://s1.gazo.cc/up/211491.png
aglkjggg

2016/09/18 13:58 編集

質問には 「●実現したいこと 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不要です。
manbo

2016/09/18 17:14

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

0

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

【参考】

投稿2016/09/18 01:12

htsign

総合スコア870

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

manbo

2016/09/18 10:21

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

2016/09/18 12:31 編集

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

2016/09/18 17:23

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問