자동화 셸 창, 주로 Internet Explorer 창에 대한 작업을 시작한 지 오래되었습니다. 이 배에있는 WebBrowser컨트롤 또는 MFC 클래스 CHTMLView내 요구를 충족 것입니다,하지만 자주, 내 머리를 긁어 매입 시작해야 WebBrowser같은 구현으로, 처음부터 컨트롤을하고 내가 할 수있는 한 많은 IE 행동으로 시뮬레이션 IDocHostUIHandler를 사용하려면 자동 완성을 에서 WebBrowser제어 . 자연스러운 대안은 왜 Internet Explorer 창 자동화를 시작하지 않는 것입니까?
새로운 Internet Explorer 창
ShellExecutePaul DiLascia가 C ++ Q & A 기사 " 브라우저 감지 재검토, 툴바 정보, COM 및 MFC에서 IUnknown "에 설명 된 것처럼 이를 수행하는 가장 간단한 방법은 (Ex) 를 호출하는 것입니다 .
복사 코드 숨기기
/// As I've shown in many programs... ShellExecute(0, _T("open"), pszMyHTMLFile, 0, 0, SW_SHOWNORMAL);
그러나 새 창을 제어 할 수 없으며 프로그램을 닫은 후에 사용자에게 IE 창을 남겨 두겠습니다. 엉망을 정리하려면 먼저 어떤 창이 내 창인지 확인한 다음 새 창을 만든 후 새 창을 인계해야합니다.
또 다른 방법은 InternetExplorer Object 를 생성 및 자동화하고 필요할 때 닫는 것입니다. Microsoft 기술 자료 에는 " Internet Explorer를 자동화하여 양식 데이터를 POST하는 방법 "기사가 있으며 최종 정리를 제외하고 기본적으로 내가 원하는 것을 설명합니다. 음, IWebBrowser2 :: Quit에 대한 간단한 호출 이 그렇게 할 것입니다.
축소 복사 코드 숨기기
// create a new IE instance and show it //CComQIPtr m_pWebBrowser2; m_pWebBrowser2.CoCreateInstance(CLSID_InternetExplorer); HRESULT hr; hr = m_pWebBrowser2->put_StatusBar(VARIANT_TRUE); hr = m_pWebBrowser2->put_ToolBar(VARIANT_TRUE); hr = m_pWebBrowser2->put_MenuBar(VARIANT_TRUE); hr = m_pWebBrowser2->put_Visible(VARIANT_TRUE); if(!::PathIsURL(m_strFileToFind)) m_strFileToFind=_T("http://blog.joycode.com/jiangsheng"); COleVariant vaURL( ( LPCTSTR) m_strFileToFind); m_pWebBrowser2->Navigate2( &vaURL, COleVariant( (long) 0, VT_I4), COleVariant((LPCTSTR)NULL, VT_BSTR), COleSafeArray(), COleVariant((LPCTSTR)NULL, VT_BSTR) ); void CAutomationDlg::OnDestroy() { //close the IE window created by this //program before exit if(m_pWebBrowser2) { if(m_bOwnIE) { m_pWebBrowser2->Quit(); m_bOwnIE=FALSE; } UnadvisesinkIE(); m_pWebBrowser2=(LPUNKNOWN)NULL; } CDialog::OnDestroy(); }
질문 하나만 더 처리기 함수 에서 창을 자동화 하기 전에 몇 초 동안 사용자가 새 IE 창을 닫으면 WM_TIMER어떻게됩니까? 노출되는 IE 개체 IWebBrowser2가 없습니다. 다행히도 Microsoft 덕분에 프로그램이 중단되지는 않지만 닫을 때 알면 더 좋을 것이므로 예기치 않은 결과를 피할 수 있습니다.
Internet Explorer 이벤트 처리
Internet Explorer 개체가 종료되면 DWebBrowserEvents2 :: OnQuit 이벤트가 발생합니다. IWebBrowser2인터페이스 포인터 를 놓기에 이상적인 시간 입니다. 개체가 죽어 가고 있기 때문에 이벤트 모니터링을 중지합니다.
복사 코드 숨기기
if(m_pWebBrowser2) { UnadvisesinkIE(); m_pWebBrowser2=(LPUNKNOWN)NULL; }
현재 Internet Explorer 창에 연결
쓸모없는 일을하는 것은 내 성격 이 아니라 일을 완벽하게 하는 것은 내 성격이다. 나는라는 기사 이후에 연결해야하는 창 상관하지 않지만 " 어떻게에 연결하는 인터넷 익스플로러의 실행중인 인스턴스 방법에 연결하는 I는"같은 것을 가정, Microsoft 기술 자료에있는 " 현재 인터넷 익스플로러의 예 "가 더 유용 할 것입니다.
그렇다면 Internet Explorer 의 현재 인스턴스는 무엇입니까? 글쎄, 그것은 최신 활성 IE 창입니다. Microsoft Windows는 활성 창을 z 순서의 맨 위로 가져 오므로 모든 IE 창에서 z 순서의 맨 위에 유지됩니다. 따라서 내가해야 할 일은 z 순서 값이 가장 높은 IE 창을 찾는 것입니다. 그래서 어떤 창이 IE 창인지 먼저 알아야합니다. Spy ++을 사용하여 조사한 후 IE 창의 창 클래스 이름이 " IEFrame"인 것으로 가정하고 쉘 창의 창 클래스 이름을 얻는 함수를 작성합니다.
복사 코드 숨기기
//shell windows object will list both IE and Explorer windows //use their window class names to identify them. CString CAutomationDlg::GetWindowClassName(IWebBrowser2* pwb) { TCHAR szClassName[_MAX_PATH]; ZeroMemory( szClassName, _MAX_PATH * sizeof( TCHAR)); HWND hwnd=NULL; if (pwb) { LONG_PTR lwnd=NULL; pwb->get_HWND(&lwnd); hwnd=reinterpret_cast<HWND>(lwnd); ::GetClassName( hwnd, szClassName, _MAX_PATH); } return szClassName; }
이 문제의 나머지 부분은 간단합니다. z 축을 통해 최상위 창을 열거 IEFrame하고 셸 창 목록에 있는 창 클래스 이름이 " "인 첫 번째 인스턴스를 찾으십시오 . 그 후, 나는 IE DHTML Document Object Model (또는 DOM이 IE 윈도우가 마지막 DocumentComplete 이벤트를 발생시킨 후 사용 가능한 DOM)으로 재생하기가 까다로운 작업을 수행 하여 윈도우가 성공적으로 연결되었는지 확인합니다.
축소 복사 코드 숨기기
void CAutomationDlg::DocumentComplete(IDispatch *pDisp, VARIANT *URL) { //HTML DOM is available AFTER the //DocumentComplete event is fired. //For more information, please visit KB article //"How To Determine When a Page Is //Done Loading in WebBrowser Control" //http://support.microsoft.com/kb/q180366/ CComQIPtr<iunknown,&iid_iunknown> pWBUK(m_pWebBrowser2); CComQIPtr<iunknown,&iid_iunknown> pSenderUK( pDisp); USES_CONVERSION; TRACE( _T( "Page downloading complete:\r\n")); CComBSTR bstrName; m_pWebBrowser2->get_LocationName(&bstrName); CComBSTR bstrURL; m_pWebBrowser2->get_LocationURL(&bstrURL); TRACE( _T( "Name:[ %s ]\r\nURL: [ %s ]\r\n"), OLE2T(bstrName), OLE2T(bstrURL)); if (pWBUK== pSenderUK) { CComQIPtr pHTMLDocDisp; m_pWebBrowser2->get_Document(&pHTMLDocDisp); CComQIPtr pHTMLDoc(pHTMLDocDisp); CComQIPtr ecAll; CComPtr pTagLineDisp; if(pHTMLDoc) { CComBSTR bstrNewTitle(_T("Sheng Jiang's Automation Test")); pHTMLDoc->put_title(bstrNewTitle); pHTMLDoc->get_all(&ecAll); } if(ecAll) { ecAll->item(COleVariant(_T("tagline")), COleVariant((long)0),&pTagLineDisp); } CComQIPtr eTagLine(pTagLineDisp); if(eTagLine) { eTagLine->put_innerText( CComBSTR(_T( "Command what is yours, conquer what is not. --Kane"))); } } }</iunknown,&iid_iunknown></iunknown,&iid_iunknown>
이제 탐색은 IE와 동일한 창에서 수행됩니다.
부산물 : 현재 Windows 탐색기 창에 연결
쉘 윈도우 ShellWindows객체 목록을 살펴보면서 부산물을 얻습니다 .Windows 탐색기 윈도우에도 공통 윈도우 클래스 이름이있는 것 같습니다. 따라서 창 메커니즘 이름이 " IEFrame" 에서 " "로 약간 변경된 Windows 탐색기 창에서도 동일한 메커니즘이 작동합니다 ExploreWClass. 재생할 DHTML DOM이 없기 때문에 Windows 탐색기 창에 기존 경로를 찾아보고이 창을 인수했다고 플래그를 지정합니다.
축소 복사 코드 숨기기
//show the folder bar COleVariant clsIDFolderBar( _T("{EFA24E64-B078-11d0-89E4-00C04FC9E26E}")); COleVariant FolderBarShow(VARIANT_TRUE,VT_BOOL); COleVariant dummy; if(m_pWebBrowser2) m_pWebBrowser2->ShowBrowserBar( &clsIDFolderBar,&FolderBarShow,&dummy); //browse to a given folder CComQIPtr<IServiceProvider> psp(m_pWebBrowser2); CComPtr<IShellBrowser> psb; if(psp) psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser,(LPVOID*)&psb); if(psb) { USES_CONVERSION; LPITEMIDLIST pidl=NULL; SFGAOF sfgao; SHParseDisplayName (T2OLE(m_strFileToFind), NULL,&pidl,0, &sfgao); if(pidl==NULL) ::SHGetSpecialFolderLocation(m_hWnd, CSIDL_DRIVES,&pidl); m_pidlToNavigate=NULL; if(pidl) { //if the start address is a folder, then browse it. //otherwise browse to its parent folder, //and select it in the folder view. LPCITEMIDLIST pidlChild=NULL; CComPtr<IShellFolder> psf; HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (LPVOID*)&psf, &pidlChild); if (SUCCEEDED(hr)){ SFGAOF rgfInOut=SFGAO_FOLDER; hr=psf->GetAttributesOf(1,&pidlChild,&rgfInOut); if (SUCCEEDED(hr)){ m_pidlToNavigate=ILClone(pidl); if(rgfInOut&SFGAO_FOLDER){//this is a folder psb->BrowseObject(pidl,SBSP_SAMEBROWSER); } else { //this is a file, browse to the parent folder LPITEMIDLIST pidlParent=ILClone(pidl); ::ILRemoveLastID(pidlParent); psb->BrowseObject( pidlParent, SBSP_SAMEBROWSER); ILFree(pidlParent); } } } //clean up ILFree(pidl); } }:
이 코드는 파일과 폴더에 대해 다른 조치를 취하고 싶기 때문에 조금 장황합니다. IShellBrowser :: BrowseObject 를 호출 pidl하고 파일을 메서드에 전달하면 Windows 탐색기는 창 탐색기 창의 주소 표시 줄에 파일 경로를 입력하는 것과 정확히 동일하게 파일을 열지 여부를 묻습니다. Enter를 누르십시오. 폴더보기에서 파일을 선택하는 "Explorer.exe / select"의 동작을 시뮬레이트하기 위해 DocumentComplete 이벤트 핸들러에 코드를 추가 합니다.
축소 복사 코드 숨기기
if(m_pidlToNavigate) { //If the start address is a file, browse to the parent folder //and then select it CComQIPtr<IServiceProvider> psp(m_pWebBrowser2); CComPtr<IShellBrowser> psb; CComPtr<IShellView> psv; if(psp) psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser,(LPVOID*)&psb); if(psb) psb->QueryActiveShellView(&psv); if(psv) { LPCITEMIDLIST pidlChild=NULL; CComPtr<IShellFolder> psf; SFGAOF rgfInOut=SHCIDS_ALLFIELDS; HRESULT hr = SHBindToParent(m_pidlToNavigate, IID_IShellFolder, (LPVOID*)&psf, &pidlChild); if (SUCCEEDED(hr)){ hr=psf->GetAttributesOf(1,&pidlChild,&rgfInOut); if (SUCCEEDED(hr)){ if((rgfInOut&SFGAO_FOLDER)==0){ //a file, select it hr=psv->SelectItem(ILFindLastID(m_pidlToNavigate) ,SVSI_SELECT|SVSI_ENSUREVISIBLE|SVSI_FOCUSED| SVSI_POSITIONITEM); } } } } //clean up ILFree(m_pidlToNavigate); m_pidlToNavigate=NULL; }
새로운 Windows 탐색기 창
우리의 새로운 성과를 이전 문제로 되돌려 봅시다. 현재 Internet Explorer 창에 연결하는 것과 거의 같은 방식으로 현재 Windows 탐색기 창에 연결할 수 있으므로 새 Internet Explorer 창을 만들고 자동화하는 방법과 유사한 새 Windows 탐색기 창을 만들고 자동화 할 수 있습니까? 놀랍게도 대답은 '아니요'입니다. 이러한 COM 개체를 만드는 Windows 탐색기에는 클래스 ID가 없습니다. 여전히 IE 창을 만들고 폴더로 이동하여 Windows 탐색기 창처럼 보이도록 폴더 탐색기 표시 줄을 표시 할 수 있지만 창 클래스 이름 " IEFrame"을 (를) 변경할 수 없으므로 표시되는 다른 IE 창과 구별 할 수 없습니다 HTML 페이지와 활성 문서는 어렵습니다.
COM 방식으로 만들 수없는 경우에도 기존 방식으로 시도 할 수 있습니다. Paul DiLascia가 " 메인 창 가져 오기 , EXE 이름 가져 오기 "기사에서 지적한 것처럼 explorer.exe 프로세스를 작성 하고 기본 창을 찾아 문서화되지 않은 메시지 WM_GETISHELLBROWSER 를 보내 새 창의 IShellBrowser 인터페이스 를 가져올 수 있습니다.
복사 코드 숨기기
//start the new process 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). _T("explorer.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. ) //wait a graceful time //so the window is created and is ready to answer messages. ::WaitForInputIdle(pi.hProcess,1000); //m_hExplorerProcess=(DWORD)pi.hProcess; EnumWindows(EnumWindowsProc,(LPARAM)this);
복사 코드 숨기기
BOOL CALLBACK CAutomationDlg::EnumWindowsProc( HWND hwnd,LPARAM lParam) { CAutomationDlg* pdlg=(CAutomationDlg*)lParam; DWORD pidwin; GetWindowThreadProcessId(hwnd, &pidwin); if (pidwin==pdlg->m_hExplorerProcess) { IShellBrowser* psb= (IShellBrowser*)::SendMessage(hwnd,WM_USER+7,0,0); CComQIPtr<IWebBrowser2> pwb(psb); return FALSE; } return TRUE; }
죄송합니다. 컴퓨터의 창도 잡히지 않습니다. 어떻게 된 거예요? Windows 탐색기의 내 폴더 옵션 페이지에서 "같은 창에서 각 폴더 열기"옵션이 선택되어 기존 Windows 탐색기 프로세스에서 새 Windows 탐색기 창이 작성됩니다. 막 다른 골목처럼 보인다.
잠깐, 다른 손에 ShellWindows물건이 있어요 모든 Windows 탐색기 창과 해당 IWebBrowser2인터페이스를 포함한 쉘 창 목록과 해당 인터페이스의 문을 제공 할 수 IShellBrowser있습니다. 이제 explorer.exe 프로세스 를 작성하기 전과 그 직후에 하나씩 두 개의 쉘 창 목록을 가져와야 합니다. 새 쉘 창을 찾기 위해 그것들을 비교해야합니다.
복사 코드 숨기기
m_pShellWindows.CoCreateInstance(CLSID_ShellWindows); if(m_pShellWindows) { //get the list of running IE windows //using the ShellWindows collection //For more information, please visit //http://support.microsoft.com/kb/176792 long lCount=0; m_pShellWindows->get_Count(&lCount); for(long i=0;i<lcount;i++) {="" ccomptr<idispatch=""> pdispShellWindow; m_pShellWindows->Item(COleVariant(i), &pdispShellWindow); if(pdispShellWindow) { m_listShellWindows.AddTail( new CComQIPtrIDispatch(pdispShellWindow)); } } }</lcount;i++)>
축소 복사 코드 숨기기
//enumerate through the new shell window list long lCount=0; m_pShellWindows->get_Count(&lCount); for(long i=0;i<lcount;i++) {="" search="" the="" new="" window="" using="" shellwindows="" collection="" for="" more="" information,="" please="" visit="" <a="" href="http://support.microsoft.com/kb/176792">http://support.microsoft.com/kb/176792 BOOL bFound=FALSE; CComPtr pdispShellWindow; m_pShellWindows->Item(COleVariant(i), &pdispShellWindow); //search it in the old shell window list POSITION pos=m_listShellWindows.GetHeadPosition(); while(pos) { CComQIPtrIDispatch* pDispatch= m_listShellWindows.GetNext(pos); if(pDispatch&&pdispShellWindow.p==pDispatch->p) { bFound=TRUE;break; } } if(!bFound)//new window found { //attach to it m_pWebBrowser2=pdispShellWindow; m_bOwnIE=TRUE; //sink for the Quit and DocumentComplete events AdviseSinkIE(); NavigateToSamplePage(FALSE); } }</lcount;i++)>
잠시만 요. "explorer.exe 프로세스를 생성 한 직후"는 무슨 뜻입니까? CreateProcess함수를 호출 한 후 1 초 ? 아니면 둘? 사실, 각 쉘 윈도우가 생성 된 후 객체에 WindowRegistered의해 이벤트가 발생 ShellWindows하고 이벤트 핸들러에 비교를 넣습니다.
복사 코드 숨기기
//sink DShellWindowsEvents events LPUNKNOWN pUnkSink = GetIDispatch(FALSE); m_pShellWindows.CoCreateInstance(CLSID_ShellWindows); AfxConnectionAdvise((LPUNKNOWN)m_pShellWindows, DIID_DShellWindowsEvents,pUnkSink, FALSE,&m_dwCookieShellWindows);
축소 복사 코드 숨기기
void CAutomationDlg::WindowRegistered(long lCookie) { //ok, a new shell window is created if(m_pShellWindows) { //enumerate through the new shell window list long lCount=0; m_pShellWindows->get_Count(&lCount); for(long i=0;i<lcount;i++) {="" search="" the="" new="" window="" using="" shellwindows="" collection="" for="" more="" information,="" please="" visit="" <a="" href="http://support.microsoft.com/kb/176792">http://support.microsoft.com/kb/176792 BOOL bFound=FALSE; CComPtr pdispShellWindow; m_pShellWindows->Item(COleVariant(i), &pdispShellWindow); //search it in the old shell window list POSITION pos=m_listShellWindows.GetHeadPosition(); while(pos) { CComQIPtrIDispatch* pDispatch= m_listShellWindows.GetNext(pos); if(pDispatch&&pdispShellWindow.p==pDispatch->p) { bFound=TRUE;break; } } if(!bFound)//new window { //attach to it m_pWebBrowser2=pdispShellWindow; m_bOwnIE=TRUE; //sink for the Quit and DocumentComplete events AdviseSinkIE(); NavigateToSamplePage(FALSE); } } //clean up if(m_dwCookieShellWindows!= 0) { LPUNKNOWN pUnkSink = GetIDispatch(FALSE); AfxConnectionUnadvise((LPUNKNOWN)m_pShellWindows, DIID_DShellWindowsEvents, pUnkSink, FALSE, m_dwCookieShellWindows); m_dwCookieShellWindows= 0; } POSITION pos=m_listShellWindows.GetHeadPosition(); while(pos) { CComQIPtrIDispatch* pDispatch= m_listShellWindows.GetNext(pos); delete pDispatch; } m_listShellWindows.RemoveAll(); m_pShellWindows=(LPUNKNOWN)NULL; } }</lcount;i++)>
브라우저 도우미 개체가 아닌 이유
새 창이 내 프로세스에 없으므로 COM 호출의 프로세스 간 마샬링 페널티가 높습니다. 자동화 작업이 너무 많은 COM 호출로 구성된 경우 BHO (브라우저 도우미 개체) 작성과 같은 코드를 처리 중으로 만들어야 할 수 있습니다 . 그러나 BHO는 Windows 탐색기와 Internet Explorer의 모든 인스턴스에 의해로드되며 전체 혼란을 해결하기 위해 전체 시스템의 속도를 늦추고 싶지 않습니다. 일부 사람들은 실제로이 기술을 사용하여 현재 Internet Explorer 인스턴스에 연결했습니다 .
알려진 문제
ShellWindows기본 경우 객체는 사용할 수없는 Explorer.exe에서의 프로세스가 살해되거나 시작되지 않습니다. 그러한 경우 BHO가 대안이 될 수 있습니다.
'C#.Net' 카테고리의 다른 글
Windows 작업 스케줄 API (0) | 2020.12.08 |
---|---|
Windows 암호정책 확인하는 Class (0) | 2020.12.01 |
GoTo 키워드 사용하지 않고 재시도(Retry) 하는 로직 (0) | 2020.09.24 |
한글 형태소 분석 (0) | 2020.09.21 |
C# Winform 프로그래밍에서 IE 띄우기 (0) | 2020.02.27 |