3

従来のwin32 winformアプリケーションによって「パイロット」されるべきwpfアプリケーションがあります。(私たちはコードを所有していません;))

レガシー アプリケーションは、ini ファイルに記述された構成パラメーターとして提供する必要がある windowsclass 名を使用して、アプリケーションをパイロットする必要があります (最小化、前面への移動、シャットダウンなど)。

問題は、Spy++ が提供するクラス名を挿入しても何も起こらないため、wpf で動作させることができないことです。ポイントは、Spi++が次のようなものを返すことです

 HwndWrapper[MyWpfProgram.exe;;16978ce2-3b8d-4c46-81ee-e1c6d6de4e6d]

GUIDは実行ごとにランダムに生成されます。

この問題を解決する方法はありますか?

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

4

3 に答える 3

2

私が頼んだことをする方法はありません。しかし、私たちは回避策を見つけました。Windowsフォーム内にxamlウィンドウを「単純に」埋め込む。

私たちが従った手順は次のとおりです。

1 - プロジェクトに Windows フォームを追加します。

2 - app.xaml を削除し、新しいフォームをアプリケーションのエントリ ポイントにします。

3 - main.xaml の hwnd が必要なので、このプロップをそのコード ビハインドに追加しました

    public IntPtr Hwnd
    {
        get { return new WindowInteropHelper(this).Handle; }
    }

4 - 次に、フォームのコンストラクターから wpf ウィンドウ クラスのインスタンスを作成します。

    private Main app;
    public ContainerForm()
    {
        InitializeComponent();

        app = new Main();
        ElementHost.EnableModelessKeyboardInterop(app);
    }

私たちが必要としていた

 ElementHost.EnableModelessKeyboardInterop(app);

すべてのキーボード入力を Windows フォームから xaml ウィンドウに渡すため

5 - ここで、xpf ウィンドウを winform に結合します。これを行うには、Windows Api を使用する必要があり、フォームの OnShow イベントで行います (理由は後で説明します)。

    [DllImport("user32.dll", SetLastError = true)]
    private static extern long SetFocus(IntPtr hWnd);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);


    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);

    [DllImport("user32.dll", EntryPoint = "SetWindowLongA", SetLastError = true)]
    private static extern long SetWindowLong(IntPtr hwnd, int nIndex, long dwNewLong);
    private const int GWL_STYLE = (-16);
    private const int WS_VISIBLE = 0x10000000;

    private void ContainerForm_Shown(object sender, EventArgs e)
    {
        app.Show();
        SetParent(app.Hwnd, this.Handle);
        SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);
        MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);

        SetFocus(app.Hwnd);
    }

 SetParent(app.Hwnd, this.Handle);

wo do the magic, then with

  SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);

wpf ウィンドウからすべてのクロムを削除します (ウィンドウが境界線なしで定義されていても境界線があります。理由は聞かないでください)。

次に、wpf ウィンドウが winform のすべてのクライアント領域を埋めるようにします。

  MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);

次に、wpf ウィンドウにフォーカスします

 SetFocus(app.Hwnd);

それが、ショーイベントですべてを行う理由です。フォームのコンストラクターでこれを行うと、winform のメイン ウィンドウがオペレーティング システムからフォーカスを取得したため、wpf ウィンドウはフォーカスを失います。

この時点で他の API 呼び出しを追加する必要がある理由がわかりませんでしたが、それらをコンストラクターに残したままにしておくと、このトリックは機能しませんでした。

とにかく、問題は解決しました;)

于 2013-08-07T15:34:43.403 に答える
1

HwndSourceを使用します。

ネイティブ Windows API 呼び出しを使用して、予想されるクラス名でウィンドウを作成し、HwndSource を使用して WPF コンテンツをそれに追加できます。

var source = HwndSource.FromHwnd(nativeWindowHandle);
source.RootVisual = mainGrid;

WPF ウィンドウを使用する必要がある場合は、「プロキシ」ウィンドウでこれを解決できると思いますが、きれいではありません。

  • WPF アプリケーションがネイティブのメッセージのみのウィンドウを生成するようにします。
  • HwndSource.AddHookを使用して、ネイティブ ウィンドウで WM_CLOSE、WM_SIZE などのメッセージを処理し、それらを「実際の」WPF ウィンドウに渡します。
于 2013-08-07T15:19:05.977 に答える
0

ウィンドウ ハンドル、タイトル、およびクラス名について、Spy++ はかなり単純な Windows API を使用します。

FindWindowEx http://msdn.microsoft.com/en-us/library/windows/desktop/ms633500%28v=vs.85%29.aspx

EnumWindows http://msdn.microsoft.com/en-us/library/windows/desktop/ms633497%28v=vs.85%29.aspx

GetClassName http://msdn.microsoft.com/en-us/library/windows/desktop/ms633582%28v=vs.85%29.aspx

「ローダー」プログラムを作成できます...

  • wpf アプリを起動する
  • 上記の API を使用して、適切なクラス名とウィンドウ ハンドルを取得します。
  • 従来の INI を編集する
  • レガシー アプリを起動する
于 2013-08-07T12:55:49.683 に答える