WPF 창에서 외부 앱 호스팅
우리는 사용자가 이동/크기 조정/등을 할 수 있는 뷰포트를 가진 WPF의 레이아웃 관리자를 개발하고 있습니다.뷰포트는 일반적으로 레이아웃 관리자에서 당사가 제어하는 공급자를 통해 데이터(사진/영화/등)로 채워집니다.제가 하는 일은 외부 Windows 앱(예: 메모장, 계산, Adobe Reader 등)을 뷰포트에 호스팅하는 것도 가능한지 검사하는 것입니다.저는 여러 가지 문제에 부딪힙니다.
대부분의 리소스는 HwndHost 클래스 사용을 가리킵니다.저는 마이크로소프트 자체의 이 단계를 실험하고 있습니다. http://msdn.microsoft.com/en-us/library/ms752055.aspx
목록 상자가 외부 응용 프로그램의 창 핸들로 대체되도록 수정했습니다.다음과 같은 질문에 대해 도움을 줄 수 있는 사람이 있습니까?
- 에는 정적 , 에는 워스루정하창추다가니합을위적크가 포함되어 있습니다.
ListBox
i▁apps. 외부앱에는 필요하지 것 같아요.저는 외부 앱에는 그것이 필요하지 않다고 생각합니다.하위 .dll에서하여 user32., user32.dll을 설정합니다. (user32.dll에서 Get/SetWindowLong을 설정합니다.)GWL_STYLE
~하듯이WS_CHILD
( 에) .WS_CHILD
더 이상 입력을 수신하지 않습니다. - 만약 내가 서브 윈도우를 사용하고, 외부 앱을 그런 것들의 아이로 만들면 합리적으로 작동하지만, 때때로 외부 앱이 그림을 그리지 않습니다.
- 또한 뷰포트 크기를 조정하려면 하위 창이 필요합니다.이것이 가능합니까?
- 앱이 창할 때 이은 " " " " (으)로 .
HwndHost
(따라서 뷰포트 외부로 이동할 수 있습니다.)제가 그것을 막을 방법이 없을까요? - 외부 응용 프로그램과 레이아웃 관리자 간에 더 이상의 상호 작용이 필요하지 않으므로 메시지를 수신하고 전달할 필요가 없다고 가정하는 것이 맞습니까?(여기서는 HwndSourceHook을 하위 창에 추가하여 목록 상자의 선택 변경 사항을 포착합니다.)
- (수정되지 않은) VS2010 예제를 실행하고 창을 닫으면 VS2010에서 프로그램이 종료되지 않습니다.만약 당신이 모든 것을 부수면, 당신은 소스 없이 조립하게 됩니다.뭔가 냄새나는 일이 벌어지고 있지만 찾을 수가 없습니다.
- 이 과정 자체는 매우 엉성하게 코드화되어 있는 것처럼 보이지만, 이 주제에 대한 더 나은 문서를 찾을 수 없습니다.다른 예는?
- 다른 접근 방식은 사용하지 않는 것입니다.
HwndHost
그렇지만WindowsFormHost
여기서 논하는 바와 같이작동하지만(그리고 훨씬 간단합니다!) 애플리케이션 크기를 제어할 수 없습니다.또한 WinFormHost는 실제로 이를 위한 것이 아닙니까?
올바른 방향으로 안내해 주셔서 감사합니다.
글쎄요... 질문이 20년 전처럼 제기되었다면, "네, 'OLE'를 보세요!"라고 대답했을 것입니다. 여기 "오브젝트 링크 및 임베딩"이 무엇인지에 대한 링크가 있습니다.
http://en.wikipedia.org/wiki/Object_Linking_and_Embedding
이 기사를 읽으면 이 사양이 정의된 인터페이스의 수를 볼 수 있습니다. 이는 작성자가 이 사양이 재미있다고 생각했기 때문이 아니라 일반적인 경우에는 기술적으로 달성하기 어렵기 때문입니다.
실제로 일부 앱(대부분 마이크로소프트 앱)에서 여전히 지원됩니다(마이크로소프트가 OLE의 거의 유일한 스폰서였기 때문에...)
애플리케이션 내부에서 OLE 서버(즉, 다른 프로세스로 실행 중인 외부 애플리케이션)를 시각적으로 호스팅할 수 있는 구성 요소인 DSOFramer(MS KB311765 및 DsoFramer가 MS 사이트에서 누락된 링크 참조)를 사용하여 이러한 애플리케이션을 포함할 수 있습니다.마이크로소프트가 몇 년 전에 발표한 일종의 대규모 해킹으로 바이너리를 찾기가 상당히 어려울 정도로 더 이상 지원되지 않습니다.
아직도 단순한 OLE 서버에서 작동하지만 Word 2010과 같은 새로운 Microsoft 애플리케이션에서는 작동하지 않는 것으로 알고 있습니다.따라서 DSOFramer를 지원하는 애플리케이션에 DSOFramer를 사용할 수 있습니다.드셔보세요.
오늘날 우리가 살고 있는 현대 세계에서 다른 애플리케이션의 경우, 애플리케이션을 호스팅하지 않고 외부 프로세스에서 실행하고 구성 요소를 호스팅하며 일반적으로 프로세스에서 실행해야 합니다.그렇기 때문에 여러분은 일반적으로 여러분이 하고 싶은 것을 하는 데 큰 어려움을 겪을 것입니다.최신 버전의 Windows(윈도우)에서 직면하게 될 한 가지 문제는 보안입니다. 신뢰하지 않는 프로세스가 어떻게 내 프로세스에서 만든 창과 메뉴를 합법적으로 처리할 수 있습니까? :-)
그래도 다양한 윈도 해킹을 이용해 애플리케이션별로 꽤 많은 애플리케이션을 할 수 있습니다.SetParent는 기본적으로 모든 해킹의 어머니입니다 :-)
다음은 사용자가 가리키는 샘플을 확장하고 자동 크기 조정을 추가하며 캡션 상자를 제거하는 코드입니다.여기에서는 다음과 같이 시스템 메뉴인 제어 상자를 암시적으로 제거하는 방법을 보여 줍니다.
public partial class Window1 : Window
{
private System.Windows.Forms.Panel _panel;
private Process _process;
public Window1()
{
InitializeComponent();
_panel = new System.Windows.Forms.Panel();
windowsFormsHost1.Child = _panel;
}
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32")]
private static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent);
[DllImport("user32")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private const int SWP_NOZORDER = 0x0004;
private const int SWP_NOACTIVATE = 0x0010;
private const int GWL_STYLE = -16;
private const int WS_CAPTION = 0x00C00000;
private const int WS_THICKFRAME = 0x00040000;
private void button1_Click(object sender, RoutedEventArgs e)
{
button1.Visibility = Visibility.Hidden;
ProcessStartInfo psi = new ProcessStartInfo("notepad.exe");
_process = Process.Start(psi);
_process.WaitForInputIdle();
SetParent(_process.MainWindowHandle, _panel.Handle);
// remove control box
int style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE);
style = style & ~WS_CAPTION & ~WS_THICKFRAME;
SetWindowLong(_process.MainWindowHandle, GWL_STYLE, style);
// resize embedded application & refresh
ResizeEmbeddedApp();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
if (_process != null)
{
_process.Refresh();
_process.Close();
}
}
private void ResizeEmbeddedApp()
{
if (_process == null)
return;
SetWindowPos(_process.MainWindowHandle, IntPtr.Zero, 0, 0, (int)_panel.ClientSize.Width, (int)_panel.ClientSize.Height, SWP_NOZORDER | SWP_NOACTIVATE);
}
protected override Size MeasureOverride(Size availableSize)
{
Size size = base.MeasureOverride(availableSize);
ResizeEmbeddedApp();
return size;
}
}
이는 기본적으로 모든 Windows "기존" 해킹입니다.http://support.microsoft.com/kb/110393/en-us (양식의 제어 메뉴 상자에서 메뉴 항목을 제거하는 방법)에서 설명한 대로 마음에 들지 않는 항목 메뉴를 제거할 수도 있습니다.
또한 "notepad.exe"를 "winword.exe"로 대체할 수 있으며 작동하는 것 같습니다.그러나 여기에는 제한이 있습니다(키보드, 마우스, 포커스 등).
행운을 빕니다.
Simon Mourier의 대답은 매우 잘 쓰여져 있습니다.하지만 제가 직접 만든 윈폼 앱으로 시도해보니 실패했습니다.
_process.WaitForInputIdle();
로 대체할 수 있습니다.
while (_process.MainWindowHandle==IntPtr.Zero)
{
Thread.Sleep(1);
}
그리고 모든 것이 순조롭게 진행됩니다.
좋은 질문 감사드리며, 답변해주신 모든 분들께 감사드립니다.
이 스레드의 답을 읽고 직접 시행착오를 거친 후에 저는 꽤 잘 작동하는 것으로 결론을 내렸지만, 물론 어떤 것들은 특별한 경우를 위해 여러분의 주의가 필요할 것입니다.
HwndHostEx를 호스트 클래스의 기본 클래스로 사용했습니다. 여기에서 찾을 수 있습니다. http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/69631#1034035
코드 예제:
public class NotepadHwndHost : HwndHostEx
{
private Process _process;
protected override HWND BuildWindowOverride(HWND hwndParent)
{
ProcessStartInfo psi = new ProcessStartInfo("notepad.exe");
_process = Process.Start(psi);
_process.WaitForInputIdle();
// The main window handle may be unavailable for a while, just wait for it
while (_process.MainWindowHandle == IntPtr.Zero)
{
Thread.Yield();
}
HWND hwnd = new HWND(_process.MainWindowHandle);
const int GWL_STYLE = -16;
const int BORDER = 0x00800000;
const int DLGFRAME = 0x00400000;
const int WS_CAPTION = BORDER | DLGFRAME;
const int WS_THICKFRAME = 0x00040000;
const int WS_CHILD = 0x40000000;
int style = GetWindowLong(notepadHandle, GWL_STYLE);
style = style & ~WS_CAPTION & ~WS_THICKFRAME; // Removes Caption bar and the sizing border
style |= WS_CHILD; // Must be a child window to be hosted
NativeMethods.SetWindowLong(hwnd, GWL.STYLE, style);
return hwnd;
}
protected override void DestroyWindowOverride(HWND hwnd)
{
_process.CloseMainWindow();
_process.WaitForExit(5000);
if (_process.HasExited == false)
{
_process.Kill();
}
_process.Close();
_process.Dispose();
_process = null;
hwnd.Dispose();
hwnd = null;
}
}
HWND, NativeMethods 및 열거형은 DwayneNeed 라이브러리(Microsoft)에서도 제공됩니다.드웨인 니드.사용자32).
WPF 창에서 NotepadHwndHost를 자식으로 추가하기만 하면 해당 창에서 호스트되는 메모장 창이 나타납니다.
그 해결책은 믿을 수 없을 정도로 관련되어 있습니다.많은 코드.몇 가지 힌트를 드리겠습니다.
우선, 당신은 올바른 방향으로 가고 있습니다.
HwndHost와 HwndSource 같은 것을 사용해야 합니다.그렇지 않으면 시각적인 유물을 얻을 수 있습니다.깜박이처럼.경고입니다. 만약 당신이 호스트와 소스를 사용하지 않는다면, 그것은 작동할 것처럼 보일 것이지만, 결국에는 작동하지 않을 것입니다. 그것은 무작위로 작고 멍청한 버그를 가지고 있을 것입니다.
이것을 보고 힌트를 얻으세요.완전하지는 않지만 올바른 방향으로 가도록 도와줄 것입니다.http://microsoftdwayneneed.codeplex.com/SourceControl/changeset/view/50925#1029346
당신은 Win32에 접속해야 당신이 요구하는 많은 것들을 통제해야 합니다.메시지를 받아 전달해야 합니다.하위 창을 "소유"하는 창을 제어해야 합니다.
Spy++를 많이 사용하세요.
저는 이것을 운영 환경에서 실행하고 있으며 지금까지 WPF 애플리케이션에 적합합니다. 꼭전세요에 전화하세요.SetNativeWindowInWPFWindowAsChild()
UI 레에서를 한 UI window
.
public static bool SetNativeWindowInWPFWindowAsChild(IntPtr hWndNative, Window window)
{
UInt32 dwSyleToRemove = WS_POPUP | WS_CAPTION | WS_THICKFRAME;
UInt32 dwExStyleToRemove = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE;
UInt32 dwStyle = GetWindowLong(hWndNative, GWL_STYLE);
UInt32 dwExStyle = GetWindowLong(hWndNative, GWL_EXSTYLE);
dwStyle &= ~dwSyleToRemove;
dwExStyle &= ~dwExStyleToRemove;
SetWindowLong(hWndNative, GWL_STYLE, dwStyle | WS_CHILD);
SetWindowLong(hWndNative, GWL_EXSTYLE, dwExStyle);
IntPtr hWndOld = SetParent(hWndNative, new WindowInteropHelper(window).Handle);
if (hWndOld == IntPtr.Zero)
{
System.Diagnostics.Debug.WriteLine("SetParent() Failed -> LAST ERROR: " + Marshal.GetLastWin32Error() + "\n");
}
return hWndOld != IntPtr.Zero;
}
여기 제가 사용한 네이티브 Win32 API가 있습니다.(창이 세팅된 후에 크기를 조절하고 초점을 맞추기 때문에 여기에 여분이 있습니다)
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
private static extern UInt32 SetWindowLong(IntPtr hWnd, int nIndex, UInt32 dwNewLong);
[DllImport("user32.dll")]
private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
private static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
private static int GWL_STYLE = -16;
private static int GWL_EXSTYLE = -20;
private static UInt32 WS_CHILD = 0x40000000;
private static UInt32 WS_POPUP = 0x80000000;
private static UInt32 WS_CAPTION = 0x00C00000;
private static UInt32 WS_THICKFRAME = 0x00040000;
private static UInt32 WS_EX_DLGMODALFRAME = 0x00000001;
private static UInt32 WS_EX_WINDOWEDGE = 0x00000100;
private static UInt32 WS_EX_CLIENTEDGE = 0x00000200;
private static UInt32 WS_EX_STATICEDGE = 0x00020000;
[Flags]
private enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040
}
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
private static readonly IntPtr HWND_TOP = new IntPtr(0);
private static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
다음에 대한 내 대답을 확인하십시오.wpf 애플리케이션 내부에서 애플리케이션을 실행하는 방법은 무엇입니까?
저는 Dwayne Need 지그재그 없이 메모장 예제를 사용할 수 있었습니다.방금 SetParent()와 boom을 추가했습니다.그녀는 드웨인니드의 예와 똑같이 일합니다.
언급URL : https://stackoverflow.com/questions/5028598/hosting-external-app-in-wpf-window
'programing' 카테고리의 다른 글
Xcode DMG 또는 XIP 파일을 다운로드하는 방법은 무엇입니까? (0) | 2023.05.05 |
---|---|
NuGet의 패키지 위치를 변경할 수 있습니까? (0) | 2023.05.05 |
MongoDB 객체용 Regex아이디 (0) | 2023.05.05 |
MongoDB: 배열의 항목 수 카운트 (0) | 2023.05.05 |
인덱스를 생성하지 않도록 Doctrine에 지정하는 방법 (0) | 2023.05.05 |