1

目的

次のコードを使用して、Windowsフォームにいくつかのアプリケーションをロードすることができました。

コード

この関数が行うことは...

  • プロセスを述べる
  • プロセスを私のフォームのパネルに埋め込む
  • 埋め込まれたプロセスを最大化する
  • パネルにサイズ変更イベントハンドラーを追加して、パネルサイズ変更時に埋め込まれたプロセスのサイズを更新します
  • フォームに閉じたイベントハンドラーを追加して、フォームを閉じるときに埋め込みプロセスを終了します

使用法

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

定数

const int   GWL_STYLE   = -16;
const long  WS_VISIBLE  = 0x10000000,
            WS_MAXIMIZE = 0x01000000,
            WS_BORDER   = 0x00800000,
            WS_CHILD    = 0x40000000;

働き

IntPtr LoadExtern(Control Panel, string Path)
{
    try
    {
        Process Process = Process.Start(Path);
        Process.WaitForInputIdle();
        IntPtr Handle = Process.MainWindowHandle;
        SetParent(Handle, Panel.Handle);
        SetWindowLong(Handle, GWL_STYLE, (int)(WS_VISIBLE+(WS_MAXIMIZE|WS_BORDER)));

        MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);

        Panel.Resize += new EventHandler(
             delegate(object sender, EventArgs e)
             {
                  MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);
             }
        );

        this.FormClosed += new FormClosedEventHandler(
             delegate(object sender, FormClosedEventArgs e) {
                  SendMessage(Handle, 83, 0, 0);
                  Thread.Sleep(1000);
                  Handle = IntPtr.Zero;
             }
        );

        return Handle;
    }
    catch (Exception e) { MessageBox.Show(this, e.Message, "Error"); }
    return new IntPtr();
}

DLLのインポート

[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr Handle, int x, int y, int w, int h, bool repaint);

[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr Handle, int Msg, int wParam, int lParam);

結果

このコードは、Windowsのメモ帳などの一部のアプリケーションでうまく機能します。メモ帳が起動し、フォームのパネルに含まれています。キャプションはなく、境界線もありません。

LoadExtern(panel1, "notepad.exe");

フォームを閉じた後、埋め込まれたプロセスは期待どおりに終了します。

問題

残念ながら、私のコードは、Firefoxやsublimetextなどの他の(より大きな)アプリケーションでは機能しません。

LoadExtern(panel2, @"C:\Program Files (x86)\Mozilla Firefox\firefox.exe");

何が起こるかというと、私のフォームが始まり、Firefoxが始まりますが、それはそれ自身のウィンドウです。アプリケーションにsublimetextまたはfirefoxを含めるのを手伝ってもらえますか?

ソリューションの一部

Sheng Jiangの回答のおかげで、さらにいくつかのアプリケーションで機能するようになりました。私がしたことは、メインウィンドウのハンドルを待つことです。

Process.WaitForInputIdle();
IntPtr Handle = new IntPtr();
for (int i = 0; Handle == IntPtr.Zero && i < 300; i++)
{
     Handle = Process.MainWindowHandle;
     Thread.Sleep(10);
}

しかし、それでもWindowsエクスプローラーのようなアプリケーションを埋め込むことはできません。

4

2 に答える 2

3

あなたのコードは偶然にうまく機能しました。

  • WaitForInputIdleは、UIスレッドを待つ必要はありません。たとえば、入力メソッドまたは他のプログラムによって作成されたフックは、UIスレッドがまだ初期化を実行している間にアイドル状態になる単純なスレッドを作成する場合があります。
  • MainWindowHandleは、最初に表示されるトップレベルウィンドウを検索します。次の場合、論理メインウィンドウは返されません。
    • メインウィンドウは、最初に作成された表示ウィンドウではありません(たとえば、ログインダイアログが最初に作成されます)
    • メインウィンドウは表示されたスタイルで作成されていません(システムトレイの通知領域にアイコンしかないプログラムについて考えてみてください)
    • メインウィンドウはまったく作成されていません(たとえば、ブラウザやWindowsエクスプローラなどの一部のアプリケーションは既存のインスタンスで新しいドキュメント/ URLを開きます)
    • メインウィンドウはありませんが、同じステータスの複数のトップレベルウィンドウがあります。IE6 / Outlook/Wordについて考えてみてください。

メインウィンドウが視覚的に作成され、実際に新しいプロセスで最初に表示されるウィンドウである場合でも、問題が発生する可能性があります。

SetParentのドキュメントから:

アプリケーションは、SetParent関数を使用して、ポップアップ、オーバーラップ、または子ウィンドウの親ウィンドウを設定できます。

トップレベルのウィンドウの親を変更できるとは言っていません。実際、トップレベルウィンドウは、プログラムが依存している可能性のある多くのサービスを提供します。

  • フルスクリーンリクエストが完了したかどうかを判断するための測定ツールとして機能します(新しいプログラムをパネル内に表示する必要があるという要件と矛盾します)
  • 新しいDDE会話の開始時、アクティブなウィンドウ/プログラムの変更時、新しいハードウェアの到着時、システム設定の変更時、ユーザーのログオフ時、Windowsエクスプローラーの起動時、ユーザーがネストされた上でEnterキーを押したときに通知を受け取るダイアログなど。トップレベルのウィンドウにのみ送信されたウィンドウメッセージのリストが長すぎて、ここにリストできません。
  • プログラムが選択した場合は、モーダルダイアログのデフォルトの所有者ウィンドウとして機能します(プログラムにモーダルダイアログを表示する場合は、クラッシュに注意してください) 。
于 2012-09-29T16:06:27.213 に答える
0

このコードはほとんどのアプリケーションで機能します。フォームのWebブラウザーコントロールを使用してファイルエクスプローラーを埋め込み、そのURLをファイルの場所に設定しました。Internet Explorerのコントロールは、魔法のようにファイルエクスプローラーに変わります。

これが私の最終的なコードです。あなた自身のプロジェクトにこれを自由に使用してください。

IntPtr EmbedProcess(Control Panel, string Path)
{
    string Name = NameFromPath(Path);

    foreach (Process Task in Process.GetProcesses())
    {
        if (NameFromPath(Task.ProcessName).Contains(Name))
        {
            try { Task.Kill(); }
            catch (Exception e) {  }
        }
    }

    try
    {
        Process Task = Process.Start(Path);
        Task.WaitForInputIdle();
        IntPtr Handle = new IntPtr();
        for (int i = 0; Handle == IntPtr.Zero && i < 10; i++) { Handle = Task.MainWindowHandle; Thread.Sleep(100); }
        SetParent(Handle, Panel.Handle);
        SetWindowLong(Handle, GWL_STYLE, (int)(WS_VISIBLE + (WS_MAXIMIZE | WS_BORDER)));

        MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true);

        Panel.Resize += new EventHandler(delegate(object sender, EventArgs e) { MoveWindow(Handle, 0, 0, Panel.Width, Panel.Height, true); });

        this.FormClosed += new FormClosedEventHandler(delegate(object sender, FormClosedEventArgs e)
        {
            SendMessage(Handle, 83, 0, 0);
            Thread.Sleep(100);
            Handle = IntPtr.Zero;
        });

        return Handle;
    }
    catch (Exception e) { MessageBox.Show(this, e.Message, "Error"); }
    return new IntPtr();
}

私は誰かがウィンドウプロセスとコンソールプロセスをフォームに埋め込むための穴C#クラスに興味があります。このgithubリポジトリをチェックしてください。

于 2012-09-29T20:45:01.123 に答える