内嵌第三方EXE程序窗口

您所在的位置:网站首页 web页面嵌入excel 内嵌第三方EXE程序窗口

内嵌第三方EXE程序窗口

2023-09-01 16:56| 来源: 网络整理| 查看: 265

近日,在做一个将一个第三方应用窗口嵌入自己的程序窗口内部的功能,经过几次摸索,终于能比较好的嵌入进去了,为方便叙述,以下称需要被嵌入应用窗口为目标窗口,承载嵌入应用窗口的为宿主窗口,目标窗口为任意第三方应用,宿主窗口为自己开发。

将目标窗口完美嵌入,关键在于将目标窗口做为宿主窗口的子窗口,这样操作系统就会帮我们完成移动,绘制等操作,为目标窗口设置父窗口,也很简单,调用一个API即可:

HWND SetParent( HWND hWndChild, HWND hWndNewParent );

第一个参数是子窗口的句柄,也即目标窗口的句柄,第二个窗口为我们想设置的目标窗口的父窗口,即宿主窗口,由于宿主窗口的程序为自己开发,要拿到这个句柄就很简单,关键在于如何找到目标窗口的句柄。

找到目标窗口的句柄,也有一个API:

HWND FindWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName );

第一个参数为窗口的类名,创建窗口之前,首先需要使用RegisterClass向系统注册一个窗口类WNDCLASS,这个结构体中有个lpszClassName字段,就代表这个窗口类的名称。第二个参数为目标窗口的标题。具体这两个值多少,可以使用vs自带的spy++工具,查看下目标窗口,就能获得。

下面分步骤,结合代码,详细展示下整体流程。

1.首先准备好宿主窗口的窗体,并且使其透明。

这个可以通过响应WM_CTLCOLOR消息,并返回一个透明画刷完成,代码很简单如下:

HBRUSH CDlgMainIntelligent::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { CDetachDlg::OnCtlColor(pDC, pWnd, nCtlColor); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)GetStockObject(NULL_BRUSH); } 2.拿到第三方应用的窗口句柄

这里又分为两种情景,一种是第三方应用尚未启动,需要我们自己去启动,并等待其启动完成后,查找其窗口句柄,另一种情形是第三方应用已启动,这样可以直接去查找窗口句柄。如何区分这两种情形,很简单,先去查找下窗口,如果能找到就证明已启动,否则就去启动下。

启动应用进程可以使用CreateProcess,这里有个问题,如何判断这个进程已经启动好了呢?可以使用

DWORD WINAPI WaitForInputIdle( __in HANDLE hProcess, __in DWORD dwMilliseconds );

不过这个仅仅是判断进程启动好,有可能主窗口还没创建完成,也有可能第三方程序崩了,压根拿不到。试过几种方法,都不太理想,我采用了最简单有效的方法,就是循环探测等待,并设置超时时间,具体看代码,我封装了一个函数,可以拿到第三方进程的句柄及主窗口句柄。

HWND GetIVAWindowAndProcess(HANDLE& hProcess) { HWND hWnd = NULL; hWnd = ::FindWindow("XXXXClient3",NULL); //是否能找到,找不到就去启动下 if(hWnd == NULL) { int nNumberDely = 10; STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); // Start the child process. if( !CreateProcess( NULL, // No module name (use command line) "XXX\\XXX.exe", // Command line NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi ) // Pointer to PROCESS_INFORMATION structure ) { AfxMessageBox(_T("启动XXX进程失败!")); return NULL; } CloseHandle(pi.hThread); WaitForInputIdle(pi.hProcess,INFINITE); hProcess = pi.hProcess; while((hWnd = ::FindWindow("XXXXClient3",NULL)) == NULL && nNumberDely > 0) { Sleep(500); nNumberDely --; } } else { DWORD dwProcessId = 0; DWORD dwThreadId = 0; dwThreadId = GetWindowThreadProcessId(hWnd,&dwProcessId); hProcess = OpenProcess(PROCESS_ALL_ACCESS,NULL,dwProcessId); } return hWnd; }

通过以上函数,就能拿到句柄了。接下来就可以设置其父窗口了。

 

3.处理窗口WM_SIZE消息

前面说过,将第三方窗口设置为子窗口,移动绘制这些我们不用管了,系统会帮忙完成,不过WM_SIZE是需要我们处理的,有了窗口句柄,这个也变得很简单。只要在OnSize里面调用MoveWindow即可

void CDlgMainIntelligent::OnSize(UINT nType, int cx, int cy) { CDetachDlg::OnSize(nType, cx, cy); if(GetSafeHwnd() == NULL) { return ; } if(!IsWindow(GetSafeHwnd())) { return; } if(!m_bIsInit) { return ; } CRect rect; GetClientRect(&rect); if(m_hIVAWnd != NULL && !m_bIsAttached) { ::SetParent(m_hIVAWnd,GetSafeHwnd()); m_bIsAttached = TRUE; } if(m_hIVAWnd) { ::PostMessage(m_hIVAWnd,WM_SYSCOMMAND,SC_MAXIMIZE,NULL); ::MoveWindow(m_hIVAWnd,rect.left,rect.top,rect.Width(),rect.Height(),TRUE); } } 4.退出时,将第三方程序关掉

好的体验就是,目标窗口完全做为宿主窗口的一部分,同生共死,因此当宿主窗口销毁的时候,目标窗口也要一并销毁。这个也很简单,只要在宿主窗口的OnCLose中做处理即可。

void CDlgMainIntelligent::OnClose() { // TODO: Add your message handler code here and/or call default if(m_hIVAWnd!= NULL) { ::PostMessage(m_hIVAWnd,WM_CLOSE,NULL,NULL); m_hIVAWnd = NULL; } if(m_hIVAProcess != NULL) { ::TerminateProcess(m_hIVAProcess,0); CloseHandle(m_hIVAProcess); m_hIVAProcess = NULL; } CDetachDlg::OnClose(); }

经过这样处理,基本能完美嵌入第三方应用的主窗口了。

另外还有一些细节上的体验,一般第三方应用窗口,也是有自己的最大最小化按钮的,需要把这个屏蔽掉,否则看起来不美观。我使用的方法是,向第三方应用注入一个DLL,在该DLL中创建一个小窗口,把应用窗口的右上角遮挡住。这样界面看起来就非常统一和协调了。 注入DLL的代码就是windows核心编程上的,没什么好说的,如下:

BOOL CDlgMainIntelligent::InjectHookDLL(HANDLE hProcess) { if(hProcess == NULL) { return FALSE; } PCWSTR pszLibFile = L"MsgHookDLL.Dll"; if(hProcess != NULL) { int cch = 1 + lstrlenW(pszLibFile) ; int cb = cch * sizeof(wchar_t); PWSTR PszLibFileRemote = (PWSTR) VirtualAllocEx(hProcess,NULL,cb,MEM_COMMIT,PAGE_READWRITE); WriteProcessMemory(hProcess,PszLibFileRemote,(PVOID)pszLibFile,cb,NULL); PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(_T("Kernel32")),"LoadLibraryW"); HANDLE hThread = CreateRemoteThread(hProcess,NULL,0,pfnThreadRtn,PszLibFileRemote,0,NULL); WaitForSingleObject(hThread,INFINITE); return TRUE; } return FALSE; }

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3