Windowsハンドル取得して操作する方法について[C#]

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
PIC

Windowsハンドル取得して操作する方法について[C#]

#1

投稿記事 by PIC » 7年前

C#で実行中のアプリケーションのWindowsハンドルを取得して、そのアプリケーションのフォームに文字などを挿入するプログラムを作りたいと思っています。したいことは2点

①電卓(windowsでcalcで開くアプリ)で1+1=2を自動実行

②ペイント(windows標準装備、mspaint)の指定した位置(100,100)から(200,200)まで線(色はなんでもいい)を引く


この二つをしたいんですが、相当てこづっています。
http://dobon.net/vb/dotnet/process/enumwindows.html
を見たりして作ろうとしてるんですが、Windowsハンドルを取得してもそこから先ができません。
①、②は実際にはC#で実装すると単純なプログラムだと思うので、プログラムの例として書いていただきたいのですが、どうか宜しくお願いします。

metaphor

Re: Windowsハンドル取得して操作する方法について[C#]

#2

投稿記事 by metaphor » 7年前

サイトのサンプルは動作しました。
>Windowsハンドルを取得してもそこから先ができません
そこまでの具体的コードを提示して頂けないでしょうか。

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace csApp2
{
    public class Program
    {
        [STAThread]
        public static void Main()
        {
            //ウィンドウを列挙する
            EnumWindows(new EnumWindowsDelegate(EnumWindowCallBack), IntPtr.Zero);

            Console.ReadLine();
        }

        public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc,
            IntPtr lparam);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowText(IntPtr hWnd,
            StringBuilder lpString, int nMaxCount);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowTextLength(IntPtr hWnd);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetClassName(IntPtr hWnd,
            StringBuilder lpClassName, int nMaxCount);

        private static bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam)
        {
            //ウィンドウのタイトルの長さを取得する
            int textLen = GetWindowTextLength(hWnd);
            if (0 < textLen)
            {
                //ウィンドウのタイトルを取得する
                StringBuilder tsb = new StringBuilder(textLen + 1);
                GetWindowText(hWnd, tsb, tsb.Capacity);

                //ウィンドウのクラス名を取得する
                StringBuilder csb = new StringBuilder(256);
                GetClassName(hWnd, csb, csb.Capacity);

                //結果を表示する
                Console.WriteLine("クラス名:" + csb.ToString());
                Console.WriteLine("タイトル:" + tsb.ToString());
            }

            //すべてのウィンドウを列挙する
            return true;
        }
    }
}

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: Windowsハンドル取得して操作する方法について[C#]

#3

投稿記事 by YuO » 7年前

PIC さんが書きました:C#で実行中のアプリケーションのWindowsハンドルを取得して、そのアプリケーションのフォームに文字などを挿入するプログラムを作りたいと思っています。
基本的に,他のプロセスに対して何かを行うというのは,そのプロセスが手段を用意してくれていない限り難しいと考えて下さい。

PIC さんが書きました:①電卓(windowsでcalcで開くアプリ)で1+1=2を自動実行
  • calc.exeのウィンドウに対してSendMessageを使ってWM_KEYDOWN, WM_KEYUP等を送信してキー動作をエミュレートする
  • calc.exeのウィンドウを前面に出して,SendKeys.Sendなどを使ってキーストロークを送信する
あたりでしょうか。
Windows 10のUWP化されたcalc.exeに対してこの方法が可能かどうかは未調査ですが……。
PIC さんが書きました:②ペイント(windows標準装備、mspaint)の指定した位置(100,100)から(200,200)まで線(色はなんでもいい)を引く
キーやマウスの操作をエミュレートすればできると思いますが,こちらはキーボードだけで行うことが難しい(無理?)ので,電卓よりもさらに難易度が高いと思います。

PIC さんが書きました:①、②は実際にはC#で実装すると単純なプログラムだと思うので、プログラムの例として書いていただきたいのですが、どうか宜しくお願いします。
全然単純ではありません。
プロセス間通信が簡単なプログラムになるのは,そのためのライブラリがちゃんと準備されている場合のみです。
オフトピック
アクセシビリティ用のAPIを使う,という方法もあるかも知れませんが,この方法は詳しくないので割愛します。
他の掲示板で魔界の仮面弁士さんが書かれているのを時々みかける程度で……。
refs)

PIC

Re: Windowsハンドル取得して操作する方法について[C#]

#4

投稿記事 by PIC » 7年前

metaphorさん、ご丁寧にありがとうございます。
ウインドウを取得するところまでは行きました。というのもサンプルプログラムを実行するだけなのでmetaphor さんが書いたコードと似たようなものです、自分で考えたコードではありません、そこから先ですがでも描画が全くできません。win32apiでフォームを用意して描画とかならサンプルプログラムで腐るほどあるのですが、実行中のアプリの上に描画ってどうすれば?って感じです…
Sendmessageでできるんでしょうか?

PIC

Re: Windowsハンドル取得して操作する方法について[C#]

#5

投稿記事 by PIC » 7年前

http://wisdom.sakura.ne.jp/system/winap ... win22.html
ここに点を描画するwin32apiのプログラムを見つけました。
C#からこのsetpixelを使えば、ペイントに描画とか可能ですか?

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: Windowsハンドル取得して操作する方法について[C#]

#6

投稿記事 by YuO » 7年前

PIC さんが書きました:http://wisdom.sakura.ne.jp/system/winapi/win32/win22.html
ここに点を描画するwin32apiのプログラムを見つけました。
C#からこのsetpixelを使えば、ペイントに描画とか可能ですか?
この方法ということは,単に描画できればよいのでしょうか。
電卓の方が「自動実行」,つまり電卓の機能を使って計算させるのが目的のようだったので,ペイントのデータとして描画したいのかと思ったのですが。

単純に,描画するだけであれば,

コード:

using System;
using System.Diagnostics;
using System.Drawing;

class Program
{
    static void Main()
    {
        foreach (var process in Process.GetProcessesByName("mspaint"))
        {
            using (var g = Graphics.FromHwnd(process.MainWindowHandle))
            {
                g.DrawLine(Pens.Black, new Point { X = 100, Y = 100 }, new Point { X = 200, Y = 200 });
            }
        }
    }
}
で描画出来ます。
まぁ,FromHwnd使っているだけなので,最小化して元に戻すと描画した内容は消えますし,保存しても描画内容は反映されませんが。

metaphor

Re: Windowsハンドル取得して操作する方法について[C#]

#7

投稿記事 by metaphor » 7年前

このプログラムを実行するとこのhttp://csi.nisinippon.com/pait.pngようになります。(もちろんSystem.Drawingの参照の追加が必要です。)私のホームページのDX.SetUserWindow(this.Handle);DX.SetUserChildWindow(panel1.Handle);の様にFormハンドルpanel子ハンドルを渡すのも面白いですよ。DXライブラリはここhttp://dixq.net/g/
http://dixq.net/forum/viewtopic.php?f=3&t=18420ここ参照してくださいね。


metaphor

Re: Windowsハンドル取得して操作する方法について[C#]

#9

投稿記事 by metaphor » 7年前

>ここに点を描画するwin32apiのプログラムを見つけました。
>C#からこのsetpixelを使えば、ペイントに描画とか可能ですか?
これを使うとすれば"マーシャリング”とか、C#はクラスが最小単位なのでC++/CLIでラッパークラスを作ると楽しい仕事が増えます。挑戦して見る価値はありますがとても難しかったですよ。

PIC

Re: Windowsハンドル取得して操作する方法について[C#]

#10

投稿記事 by PIC » 7年前

>YUOさん

using System;
using System.Diagnostics;
using System.Drawing;

class Program
{
static void Main()
{
foreach (var process in Process.GetProcessesByName("mspaint"))
{
using (var g = Graphics.FromHwnd(process.MainWindowHandle))
{
g.DrawLine(Pens.Black, new Point { X = 100, Y = 100 }, new Point { X = 200, Y = 200 });
}
}
}

で描画出来ます。
まぁ,FromHwnd使っているだけなので,最小化して元に戻すと描画した内容は消えますし,保存しても描画内容は反映されませんが。


上記についてですが、とても興味深いです。
保存しても描画内容は反映されないというのは、ペイントを保存しても空になってるってことですか?
書いた図形を例えばペイント側からGUIで横にずらして保存しても空ですか?今確かめたいんですが超劣化PCなんで無理です。

PIC

Re: Windowsハンドル取得して操作する方法について[C#]

#11

投稿記事 by PIC » 7年前

実際に描画してるスクリーンキャプチャとかアップして頂きませんか?
msとvisualc#の画面が同時に見える状態です。
大変面白い処理だと思うし自分でも確かめたいんですが、環境がもう最悪なので…
ここに書き込むのですが一苦労です…
新しいPC買ったらすぐにでも確かめたいです。ということでどうかお願いします。

PIC

Re: Windowsハンドル取得して操作する方法について[C#]

#12

投稿記事 by PIC » 7年前

欲を言えば、保存されないってのはちょっと考えられないので、
プログラム実行→ペイントに描画される→ペイント側から名前をつけて保存で開いたら描画されてるってのが理想です。
これはFromHwndを使うと無理なんでしょうか?

hide

Re: Windowsハンドル取得して操作する方法について[C#]

#13

投稿記事 by hide » 7年前

オフトピック
動かさなくてもいいですが、聞く前に教えてもらった FromHwnd について調べましたか?
聞くのはいいのですが、人に聞くことだけで進めるとなかなか成長できないですよ。
プログラマではなくてディレクターとかになりたいならまた別なのかもしれませんが。

PIC

Re: Windowsハンドル取得して操作する方法について[C#]

#14

投稿記事 by PIC » 7年前

FromHwndは単純に描画するメソッドですよね?
FromHwnd(位置情報、色、線の種類)とかで描画が可能ということですか

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: Windowsハンドル取得して操作する方法について[C#]

#15

投稿記事 by YuO » 7年前

他のアプリケーションを外部から操作したいのであれば,最低でもWin32 APIの仕組み程度は知っておいた方がよいかと。

描画に関して言えば,基本的にはGetDC等でウィンドウハンドルからデバイスコンテキストを得て描画するのではなく,
WM_PAINTメッセージがウィンドウに送られてきたときにBeginPaintして得られたデバイスコンテキストに対して描画する,という仕組みが普通です。
WinFormsでも,直接コントロールに対してCreateGraphicsするのではなく,OnPaintメソッドのオーバーライドまたはPaintイベントで描画するのと同じです。
FromHwndメソッドを使っての描画は,WM_PAINTとは全く異なるタイミングで描画することになります。
言ってみれば,Control.CreateGraphicsを使って描画したのと同じ事です。

さらに,ペイントは表示されているものをそのまま保存しているのではなく,内部に持っているデータ元を保存しているはずです。
# Windowsの仕組みとして,ウィンドウのデバイスコンテキストに描画しても消える事がある以上,本来のデータを持っておく必要がある。
そして,そのデータにアクセスするには正規の方法を採る必要があります。
正規の方法とは,マウス(等)を使って描画する,ということです。
当然,マウスの動作そのものをソフトウェアで自動化することはできないので,マウスの描画に相当する事をエミュレートしないとそれはできません。
その方法として提示したのがSendMessage等によるエミュレート方法です。

アバター
へにっくす
記事: 634
登録日時: 11年前
住所: 東京都

Re: Windowsハンドル取得して操作する方法について[C#]

#16

投稿記事 by へにっくす » 7年前

PIC さんが書きました:FromHwndは単純に描画するメソッドですよね?
FromHwnd(位置情報、色、線の種類)とかで描画が可能ということですか
FromHwndは単にハンドルを取得するだけです。
どこからそんなことになるのでしょうか?

そこに対しグラフィックスコンテキストを取得しなんか描画をしたとしても、
当のアプリ自身がそのグラフィックスコンテキストを知らないので
画像の保存なんかできませんね。

アプリがそういう手段の提供をしていない場合、
ユーザーがするキーやマウスの操作をメッセージで送るしか手はありません。
#YuOさんが初めに言った、全然単純じゃないという意味、分かっていますか。
written by へにっくす

PIC

Re: Windowsハンドル取得して操作する方法について[C#]

#17

投稿記事 by PIC » 7年前

でもフリーソフトで画像やテキストを他アプリに転送したりするものはたくさん存在しますが、技術的に可能だからではないですか?


>アプリがそういう手段の提供をしていない場合
これがよく分かりません、これは操作されないためにするセキュリティ的な処理ということでしょうか?
それともWindows自体がデフォルトで他アプリから他アプリへの処理を禁止してるんでしょうか?自分でプログラム書いて作ったキャンパスなら普通に描いて保存もできますよね。ペイントへ転送して画面に映ってるものを保存できないってのはちょっと信じられません、実際に保存できない様子とかをキャプチャしてもらえませんか?何のために転送されたか分からないじゃないですか、そんな中途半端なことって起こりえるんでしょうか、保存されない表示されるだけならそもそも転送できる意味すらないような気がします。

hide

Re: Windowsハンドル取得して操作する方法について[C#]

#18

投稿記事 by hide » 7年前

今まで話していたものが画像の転送の話ではないからです。
テレビ画面に落書きされたところでテレビ局は知ったことではないでしょう?

metaphor

Re: Windowsハンドル取得して操作する方法について[C#]

#19

投稿記事 by metaphor » 7年前

たしかにExcelやWordなどはC#プログラムから画像やテキスト転送したり普通に描いて保存もできます。PICさんの疑問は当然でありもっともなことです。実際に確かめたらすぐわかるのでC#コンソールプログラムで

コード:

using System;
using System.Diagnostics;
using System.Drawing;
 
class Program
{
    static void Main()
    {
        foreach (var process in Process.GetProcessesByName("mspaint"))
        {
            using (var g = Graphics.FromHwnd(process.MainWindowHandle))
            {
                g.DrawLine(Pens.Black, new Point { X = 100, Y = 100 }, new Point { X = 200, Y = 200 });
            }
        }
    }
}
を実行したらこうhttp://csi.nisinippon.com/pait.pngなるので”実際に試す”ところから始めましょう。
行数も僅かですから実行できますよ。そのときペイント(windows標準装備、mspaint)は起動しておきます。
またSystem.Drawingの参照の追加が必要ですから忘れないようにしてください。では試してみましょう。
分からないことが起きたら聞いてくださいね。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: Windowsハンドル取得して操作する方法について[C#]

#20

投稿記事 by YuO » 7年前

PIC さんが書きました:でもフリーソフトで画像やテキストを他アプリに転送したりするものはたくさん存在しますが、技術的に可能だからではないですか?
一例を挙げてもらえますか。
通常はクリップボードへデータを渡しているだけだと思いますが。
また,特定のソフトウェアに対してデータを渡す,というものはあり得ます。
Microsoft Officeの製品群などは,COMと呼ばれる外部インターフェースを持つため,COMとOfficeの規約に従った方法であれば外部から制御が可能です。
PIC さんが書きました:>アプリがそういう手段の提供をしていない場合
これがよく分かりません、これは操作されないためにするセキュリティ的な処理ということでしょうか?
それともWindows自体がデフォルトで他アプリから他アプリへの処理を禁止してるんでしょうか?
後者です。
他のプロセスへのアクセスを許すと,プロセスの状態を想定外の状態にされる可能性があるためです。
PIC さんが書きました:自分でプログラム書いて作ったキャンパスなら普通に描いて保存もできますよね。
できません。
描いた情報をちゃんと保持しておいて,その情報を保存しているのです。
PIC さんが書きました:ペイントへ転送して画面に映ってるものを保存できないってのはちょっと信じられません、実際に保存できない様子とかをキャプチャしてもらえませんか?何のために転送されたか分からないじゃないですか、そんな中途半端なことって起こりえるんでしょうか、保存されない表示されるだけならそもそも転送できる意味すらないような気がします。
表示されているものが正ではなく,ペイントの内部で保持しているデータが正です。
勝手に外から書き込んでも,ペイントの内部のデータは書き換わりません。

PIC

Re: Windowsハンドル取得して操作する方法について[C#]

#21

投稿記事 by PIC » 7年前

YuOさん

>自分でプログラム書いて作ったキャンパスなら普通に描いて保存もできますよね。

お絵かきソフトの自作は無理ってことですか?
保存ができないソフトって凄いですね…

PIC

Re: Windowsハンドル取得して操作する方法について[C#]

#22

投稿記事 by PIC » 7年前

皆さんありがとうございます、大変興味深い話で面白いんですが、疑問が生まれました。
もし他のアプリを操作できないのであるならばvar process in Process.GetProcessesByName("mspaint")このような記述を使う意味はあるんでしょうか?他アプリの情報を得る関数自体の存在価値がない気がします。

metaphor

Re: Windowsハンドル取得して操作する方法について[C#]

#23

投稿記事 by metaphor » 7年前

>大変面白い処理だと思うし自分でも確かめたいんですが、環境がもう最悪なので…
>ここに書き込むのですが一苦労です…
>新しいPC買ったらすぐにでも確かめたいです。ということでどうかお願いします。
VisualStudioを全く使用しなくてもWindowsさえあれば簡単にC#,VB,xaml(ザムル)のコンパイルができます。
私は1年以上前からWindowsのみで沢山のVS2015相当のアプリを作って専用のホームページにアップしてあります。
VS2015はいれたくなかったのですが3ヶ月まえ別のソフトにくっついて入りました。いまでもWindowsだけの機能
でC#,VB,xamlをコンパイルする方が軽くて早いのでよく使います。C#,VBにVisualStudioは不要です。
(ある意味邪魔で本当の理解の妨げになります。C/C++には必要です。)
FormもMSDNで推奨されているxamlで作れます。
それにいまは参照設定にはdllだけでなくexeも可能になっていてPICさんの言われることは現在の技術では全部可能です。
exeを解析するソフトもWindowsに付属しています。
(PowerShell(新コマンドプロンプト)を使えば解析も(C#コンパイラー内臓)xaml動作も可能です)
沢山の実例が保存してあります。”PICさんの本当にやりたいこと”が違っているのに気付きました。
ペイントの様なアプリを作りたいのならコンソールプログラムから簡単につくれます。

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}
これが動作ますか。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: Windowsハンドル取得して操作する方法について[C#]

#24

投稿記事 by YuO » 7年前

PIC さんが書きました:>自分でプログラム書いて作ったキャンパスなら普通に描いて保存もできますよね。
お絵かきソフトの自作は無理ってことですか?
保存するデータが,「内部で保持しているデータ」であれば保存できますが,「キャンバス」という,通常はウィンドウに表示するWidgetの話になっているので,「できない」と書いています。
あくまで保存するのは内部で持っているデータであって,描画された結果ではない,というのは理解されていますでしょうか。
PIC さんが書きました:もし他のアプリを操作できないのであるならばvar process in Process.GetProcessesByName("mspaint")このような記述を使う意味はあるんでしょうか?他アプリの情報を得る関数自体の存在価値がない気がします。
Processクラス自体は,他のプログラムを起動したり,プログラムの終了結果を得るなど,いくつかの使い道があります。
基本的には,自分で起動したプロセスを扱うためのクラスであるため,GetProcess系のメソッドの結果をあまり使う事はないです。

閉鎖

“C言語何でも質問掲示板” へ戻る