1. 程式人生 > >C#將外部視窗嵌入到WPF控制元件當中

C#將外部視窗嵌入到WPF控制元件當中

C#講外部視窗嵌入到WPF控制元件當中

本Markdown編輯器使用[StackEdit][6]修改而來,用它寫部落格,將會帶來全新的體驗哦:

  • 可能會用到的API函式
    public class Win32API
    {
        #region Win32 API
        [DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
             CharSet = CharSet.Unicode, ExactSpelling = true
, CallingConvention = CallingConvention.StdCall)] public static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll"
, SetLastError = true)] public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent); [DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)] public static extern long GetWindowLong(IntPtr hwnd, int nIndex); public static IntPtr SetWindowLong
(HandleRef hWnd, int nIndex, int dwNewLong) { if (IntPtr.Size == 4) { return SetWindowLongPtr32(hWnd, nIndex, dwNewLong); } return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); } [DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)] public static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)] public static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll", SetLastError = true)] public static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags); [DllImport("user32.dll", SetLastError = true)] public static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint); [DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)] public static extern bool PostMessage(IntPtr hwnd, uint Msg, uint wParam, uint lParam); /// <summary> /// 獲取系統錯誤資訊描述 /// </summary> /// <param name="errCode">系統錯誤碼</param> /// <returns></returns> public static string GetLastError() { var errCode = Marshal.GetLastWin32Error(); IntPtr tempptr = IntPtr.Zero; string msg = null; FormatMessage(0x1300, ref tempptr, errCode, 0, ref msg, 255, ref tempptr); return msg; } /// <summary> /// 獲取系統錯誤資訊描述 /// </summary> /// <param name="errCode">系統錯誤碼</param> /// <returns></returns> public static string GetLastErrorString(int errCode) { IntPtr tempptr = IntPtr.Zero; string msg = null; FormatMessage(0x1300, ref tempptr, errCode, 0, ref msg, 255, ref tempptr); return msg; } [System.Runtime.InteropServices.DllImport("Kernel32.dll")] public extern static int FormatMessage(int flag, ref IntPtr source, int msgid, int langid, ref string buf, int size, ref IntPtr args); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetParent(IntPtr hwnd); [DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); public const int SWP_NOOWNERZORDER = 0x200; public const int SWP_NOREDRAW = 0x8; public const int SWP_NOZORDER = 0x4; public const int SWP_SHOWWINDOW = 0x0040; public const int WS_EX_MDICHILD = 0x40; public const int SWP_NOACTIVATE = 0x10; public const int SWP_ASYNCWINDOWPOS = 0x4000; public const int SWP_NOMOVE = 0x2; public const int SWP_NOSIZE = 0x1; public const int WM_CLOSE = 0x10; public const int SW_HIDE = 0; //{隱藏, 並且工作列也沒有最小化圖示} public const int SW_SHOWNORMAL = 1; //{用最近的大小和位置顯示, 啟用} public const int SW_NORMAL = 1; //{同 SW_SHOWNORMAL} public const int SW_SHOWMINIMIZED = 2; //{最小化, 啟用} public const int SW_SHOWMAXIMIZED = 3; //{最大化, 啟用} public const int SW_MAXIMIZE = 3; //{同 SW_SHOWMAXIMIZED} public const int SW_SHOWNOACTIVATE = 4; //{用最近的大小和位置顯示, 不啟用} public const int SW_SHOW = 5; //{同 SW_SHOWNORMAL} public const int SW_MINIMIZE = 6; //{最小化, 不啟用} public const int SW_SHOWMINNOACTIVE = 7; //{同 SW_MINIMIZE} public const int SW_SHOWNA = 8; //{同 SW_SHOWNOACTIVATE} public const int SW_RESTORE = 9; //{同 SW_SHOWNORMAL} public const int SW_SHOWDEFAULT = 10; //{同 SW_SHOWNORMAL} public const int SW_MAX = 10; //{同 SW_SHOWNORMAL} //const int PROCESS_ALL_ACCESS = 0x1F0FFF; //const int PROCESS_VM_READ = 0x0010; //const int PROCESS_VM_WRITE = 0x0020; internal const int GWL_WNDPROC = (-4), GWL_HINSTANCE = (-6), GWL_HWNDPARENT = (-8), GWL_STYLE = (-16), GWL_EXSTYLE = (-20), GWL_USERDATA = (-21), GWL_ID = (-12); internal const int WS_CHILD = 0x40000000, WS_VISIBLE = 0x10000000, LBS_NOTIFY = 0x00000001, HOST_ID = 0x00000002, LISTBOX_ID = 0x00000001, WS_VSCROLL = 0x00200000, WS_BORDER = 0x00800000; private const int HWND_TOP = 0x0; private const int WM_COMMAND = 0x0112; private const int WM_QT_PAINT = 0xC2DC; private const int WM_PAINT = 0x000F; private const int WM_SIZE = 0x0005; private const int SWP_FRAMECHANGED = 0x0020; #endregion Win32 API }
  • 給該視窗新增一個容器
    public partial class PluginContainer : ContentControl
    {
        public PluginContainer(string appFileName,IntPtr hostHandle)
        {
            InitializeComponent();
            _appFilename = appFileName;
            _hostWinHandle = hostHandle;
        }

        private void OpenExternProcess(int width,int height)
        {
            try{
                ProcessStartInfo info = new ProcessStartInfo(_appFilename) ;
                info.UseShellExecute = true;
                //info.WindowStyle = ProcessWindowStyle.Minimized;
                info.WindowStyle = ProcessWindowStyle.Hidden;

                AppProcess = System.Diagnostics.Process.Start(info);
                // Wait for process to be created and enter idle condition
                AppProcess.WaitForInputIdle();
                while (AppProcess.MainWindowHandle == IntPtr.Zero){
                    Thread.Sleep(5);
                }                    
            }
            catch (Exception ex){
                throw ex;             
            }
        }
        public bool EmbedProcess(int width, int height)
        {
            OpenExternProcess(width, height);
            try {
                var pluginWinHandle = AppProcess.MainWindowHandle;//Get the handle of main window.
                embedResult = Win32API.SetParent(pluginWinHandle, _hostWinHandle);//set parent window
                Win32API.SetWindowLong(new HandleRef(this, pluginWinHandle), Win32API.GWL_STYLE, Win32API.WS_VISIBLE);//Set window style to "None".
                var moveResult = Win32API.MoveWindow(pluginWinHandle, 0, 0, width, height, true);//Move window to fixed position(up-left is (0,0), and low-right is (width, height)).
                //embed failed, and tries again
                if (!moveResult || embedResult==0){
                    AppProcess.Kill();
                    if (MAXCOUNT-->0){
                        EmbedProcess(width, height);
                    }                    
                }
                else{
                    Win32API.ShowWindow(pluginWinHandle, (short)Win32API.SW_MAXIMIZE);
                }      
            }
            catch (Exception ex)
            {
                var errorString = Win32API.GetLastError();
                MessageBox.Show(errorString+ex.Message);
            }
            return (embedResult != 0);
        }

        #region

        public int embedResult = 0;

        public Process AppProcess { get; set; }
        private  IntPtr _hostWinHandle { get; set; }

        private string _appFilename = "";

        private  int MAXCOUNT=10;

        #endregion

    }
  • 正式使用
//把控制代碼傳進去,這個mainBorder是在MainWindow中一個Border控制元件的名字,下面這個獲取控制代碼這個只能在MainWindow.xaml.cs檔案中才能獲取,我也不知道為什麼。在其他的某個xaml.cs中卻獲取不到這個控制代碼,這個視窗控制代碼被當做外部視窗的父視窗了(API中的 SetParent函式來實現的)
var hostWinHandle = ((HwndSource)PresentationSource.FromVisual(mainBorder)).Handle;
var plugin = new PluginContainer(fileName, hostWinHandle);
mainBorder.Content = plugin;
plugin.EmbedProcess((int)mainBorder.Width,(int)mainBorder.Height);``
  • *此外
    獲取視窗控制代碼還可以用API函式FindWindow來實現
IntPtr handle = FindWindow(null,"Window_Name");