Delphi WebBrowser
이 글은 제가 IE스파이를 만들면서 MSDN을 뒤져 C소스를 델파이로 바꾸고,
여기저기 기웃거리며 하나씩 주워 모은 자료입니다.
이걸 참고 하셔서 WebBrowser 컨트롤을 쓰는데 조금이라도 도움이 되길 바랍니다.
글을 메모장에서 쓰다보니 보기좋게 꾸지미 못하는점을 쪼금 이해해 주세요.
1. 기본적인 사항
모든 웹브라우저 컴퍼넌트는 IWebBrowser2 라는 인터페이스를 사용합니다.
델파이로만들던 VC로 만들던 VB로 만들던지 암튼간에 무조건 저 인터페이스를 사용합니다.
델파이의 TWebBrowser라는 컴퍼넌트 역시 MS의 웹브라우저 컴포넌트를 포장해서 쓰는관계로
IWebBrowser2 인터페이스를 가지고 있습니다. 바로 OleObject 퍼로퍼티가 그 놈입니다.
IWebBrowser2 외에도 관련된 인터페이스들이 있는데, 계보를 따져보면 아래와 같습니다.
IUnknown
|
IDispatch
|
IWebBrowser
|
IWebBrowserApp
|
IWebBrowser2
이 인터페이스들은 델파이의 컴포넌트 계보와 개념이 비슷합니다.
우리는 IWebBrowser2 의 값을 알고 있기 때문에 필요하다면
그 상위의 인터페이스들을 알아 낼수도 있습니다.
IWebBrowser2로 IWebBrowserApp를 액세스 하기위해서는 이렇게 하면 됩니다.
Web : IWebBrowser2;
WebbApp : IWebBrowserApp;
// Web에는 이미 값이 들어가 잇음
if Web.QueryInterface(IID_IWebBrowserApp, WebApp)=S_OK
then 성공적으로 얻어옴
else 실패함
이렇게 하면 됩니다. 하지만 실패할 일이 절대로 없다는 확신이 설때에는 그냥
(Web as IWebBrowserApp)
이렇게만 하면 됩니다.
2. TWebBrowser 컴포넌트 조작상에 알아두면 좋은 팁들
2.1. 컴퍼넌트 초기화 하기
웹브라우저 컴퍼넌트를 초기화하려면 무슨 문서라도 하나 읽어와야 합니다.
보통은 라는 간단한 텍스트를 a.htm 등에 담아 놓고
WebBrowser.Navigate('file://c:/.../a.htm');
이렇게 하면됩니다. 하지만 이건 저같이 무식한 사람이 앞뒤 안가리고 그냥 쓰는 방법이고
좀더 세련되게 흔적없이 하려면
WebBrowser.Navigate('about:blank');
이렇게 하면 됩니다.
2.2. 소스 보기
HTML을 읽은 브라우저에서 소스를 뽑아내기란 아주 복잡한 작업입니다.
느려터진 MSDN의 관련 클래스들을 뒤지다 보면 아마도
Document 클래스안에 DocumentElement 클래스안에
OuterHTML 이라는 프로퍼티를 찾을 수 있습니다.
소스를 메모장에다가 보여주고 싶다면
Memo1.Lines.Text := WebBrowser.OleObject.Document.DocumentElement.OuterHTML;
이렇게 하면됩니다.
이렇게 간단한데 뭐가 복잡하냐구요?
이건 소스는 소스지만 조작된 소스입니다.
IE의 HTML파서를 거쳐서 정형화된 소스입니다.
웹브라우저로 로딩을 하면 그놈이 그놈이지만 소스를 보면
분명 원래 소스와는 차이가 있죠.
그렇다면 원래 모양 그대로 뽑아낼려면 어떻게 해야할까요?
뽑아서 스트림으로 저장하는걸 보면 이래요..
function TMainForm.RevealSource(const Document : IHTMLDocument2; Buf : TStream) : Boolean;
var
WebS : IPersistStreamInit;
begin
Result := False;
if SUCCEEDED( Document.QueryInterface(IID_IPersistStreamInit, WebS) ) then
if SUCCEEDED( WebS.Save(TStreamAdapter.Create(Buf), True) ) then Result:=True;
end;
또는 한줄로 간단히
(Document as IPersistStreamInit).Save(TStreamAdapter.Create(Buf), True)
이렇게 하면 되겠죠.
파일로 저장할때는 TFileStream을 넣고 그냥 보여줄때는
TMemoryStream을 넣고 등등으로 하면 되겠네요.
--; 별로 복잡하지 않다고요?
원래는 IStream, TOleStream 등을 써서 아주 복잡하게 만들었었는데,
인터넷에 뒤져보니 저런 쉬운 방법이 있길래 저걸로 바꿨습니다.
반대로 스트림에 들어 있는 HTML을 웹브라우저로 집어 넣을때는
(Document as IPersistStreamInit).Load(TStreamAdapter.Create(Buf));
이렇게 하면 됩니다. 아주 간단하죠.
주) IHTMLDocument2 클래스를 사용하기 위해서는 MSHTML 을
uses 구문에 추가해야 합니다.
이 놈은 WebBrowser.Document 입니다.
혹은 WebBrowser.OleObject.Document
2.3. HTML 문서를 리소스에 넣기
BMP,WAVE 들도 다 리소스에 들어가는데 HTML문서라고
리소스에 들어가지 말란 법 있나요.
HTML문서를 일단 밀어 넣어 보자구요.
TEST.RC 파일에다가
HTML1 23 "html est1.htm"
HTML2 23 "html est2.htm"
이렇게 넣고 res로 컴파일합니다.
{$R TEST.RES}
이렇게 넣고
WebBrowser.Navigate('RES://' + Application.ExeName + '/HTML1');
이렇게 하면 된답니다. (저도 안 해봤습니다만)
안돼는걸 된다고 올려놓았을리는 없으니 틀림없이 되겠죠.
3. TWebBrowser말고 외부의 IWebBrowser2 인터페이스 얻어오기.
이 부분은 외부의 IE창의 웹브라우저를 조작하기 위한 부분입니다.
다 필요없고 우리가 필요한건 오직 IWebBrowser2 인터페이스 입니다.
이 놈만 얻어오면 내 프로그램의 컴포넌트처럼 조작할 수 있습니다.
전역 변수 혹은 폼 클래스 변수로 이놈을 선언 합니다.
var
FShellWindow : IShellWindows;
프로그램 초기화 루틴 혹은 폼 클래스 OnCreate 부분에 이걸 넣어 초기화 시킵니다.
FShellWindow := CreateComObject(CLASS_ShellWindows) as IShellWindows;
IShellWindows 는 윈도우상의 모든 쉘윈도우를 관리하기 위한 인터페이스입니다.
CreateComObject 는 쉘윈도우 관리자를 새로 만드는게 아니라 기존의 관리자의
인터페이스를 얻어오게 됩니다. 왜냐하면 Single Instance 이기 때문에.
우리는 이제 필요할때마다
이 관리자에게 쉘에대해 물어 보면 그때그때의 열린 쉘들을
친절하게 가르쳐 줍니다.
여기서 쉘이란 IE만을 가르키는게 아닙니다.
윈도우즈탐색기, 제어판, IE창, IE - FTP모드 창 등의
모든 쉘들을 다 가르쳐 주기때문에 웹브라우저를 선별하는 작업을 따로 해주어야합니다.
Count := FShellWindow.Count; // 현재 열린 쉘들의 갯수
if Count>0 then
For I:=0 to Count-1 do begin
if WebBrowserCheck(FShellWindow.Item(I) as IWebBrowser2) then begin
// 이 놈은 웹브라우저 이다.
end;
end;
이런식으로...
여기서 선별하는 방식은 정석인지 아닌지 모릅니다.
그냥 제가 쓰는 방법을 적은 것일 뿐입니다.
간단하게 Document 인터페이스를 가지고 있는지 아닌지로 판단 합니다.
일단 탐색기나 제어판 등은 Document 인터페이스가 없습니다.
function WebBrowserCheck(const Webb : IWebBrowser2) : Boolean;
var
WebV : Variant;
Buf : String;
begin
WebV := Webb;
Result := False;
if Assigned(Webb) then
try Buf := WebV.Document.URL;
// Document 인터페이스가 할당 되어 있지 않으면 예외오류 발생
Result := True;
except ;
end;
end;
해보지는 않았지만 어쩌면 FShellWindow.QueryInterface로 체크해보면
제대로된 IwebBrowser2를 걸러낼 수 있을지도 모르겠어요.
암튼 이렇게 IWebBrowser2를 얻어 왔다면 모든게 순조롭게 진행이 되겠죠.
예을 들어 그 IE 창을 닫아 버리고 싶다면?
Webb : IWebBrowser2;
WebbApp : IWebBrowserApp;
WebV : Variant;
일 경우
if Webb.QueryInterface(IID_IWebBrowserApp, WebApp)=S_OK then WebApp.Quit;
이렇게,
그 창의 URL을 얻어오고 싶다면
WebV := Webb;
URL := WebV.Document.URL;
여기서 잠깐..
왜 Variant 타입의 변수에 넣어서 엑세스 하냐면요.
그냥 하면 에러가 납니다. 왜냐면 Webb.Dcocument 는 IDispatch 형이기때문에
URL이란 프로퍼티가 없기 때문이죠.
물론 (Webb.Get_Document as IHTMLDocument2).URL 이렇게 해도 되겠죠.
뭐 이런건 대충 편한대로 알아서 하시기 바래요~
WebBrowser에 글추가
procedure WebString(Web: TWebBrowser; msg : string);
var tmp : string;
begin
tmp := Web.OleObject.Document.Body.innerHTML;
tmp := tmp + msg;
Web.OleObject.Document.Body.innerHTML := tmp;
Web.OleObject.Document.Body.Doscroll('PageDown');
end;
웹브라우져 컨트롤을 이용할때, enter key와 ctrl+c/x 동작 하도록 하기
IOleInPlaceActiveObject로 검색을 해보니 비슷한 문서들이 많이 있긴 하던데,
대부분 enter key에 대한 언급 뿐이더군요.
그래서 ctrl+c/ctrl+x에 대해서도 처리하도록 조금 수정 해 봤습니다.
참고하세요..
uses
ActiveX, ClipBrd;
var
Form1: TForm1;
FOleInPlaceActiveObject: IOleInPlaceActiveObject;
SaveMessageHandler: TMessageEvent;
procedure TForm1.FormActivate(Sender: TObject);
begin
SaveMessageHandler := Application.OnMessage;
Application.OnMessage := MyMessageHandler;
end;
procedure TForm1.FormDeactivate(Sender: TObject);
begin
Application.OnMessage := SaveMessageHandler;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Application.OnMessage := SaveMessageHandler;
FOleInPlaceActiveObject := nil;
end;
procedure TForm1.MyMessageHandler(var Msg: TMsg; var Handled: Boolean);
const
DialogKeys: set of Byte = [VK_LEFT, VK_RIGHT, VK_BACK, VK_UP, VK_DOWN,
$30..$39, $41..$42, $44..$55, $57, $59..$5A];
begin
{ exit if we don't get back a webbrowser object }
if WebBrowser1 = nil then
begin
Handled := False;
Exit;
end;
if (Msg.hwnd = WebBrowser1.Handle) or (IsChild(WebBrowser1.Handle, Msg.hwnd)) then
begin
if ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP)) and (GetKeyState(VK_CONTROL) < 0) then
begin
Handled := (WebBrowser1.Application as IOleInPlaceActiveObject).TranslateAccelerator(Msg)=S_OK;
if (Msg.wParam = 67) then
Clipboard.AsText := WebBrowser1.OleObject.Document.selection.createRange().text;
if (Msg.wParam = 88) then
WebBrowser1.OleObject.Document.selection.createRange().text := ';
WebBrowser1.OleObject.Document.selection.createRange().select();
end
else
begin
Handled := not word(Msg.wParam) in [byte('A')..byte('Z'),VK_RETURN];
if Handled or (Msg.wParam = VK_RETURN) then
Handled := (WebBrowser1.Application as IOleInPlaceActiveObject).TranslateAccelerator(Msg)=S_OK;
end
end;
end;
WebBrowser에 메인과 프레임 읽기 완료 알기
procedure TForm1.WebBrowser1DocumentComplete(Sender: TObject;
const pDisp: IDispatch; var URL: OleVariant);
var
CurWebrowser: IWebBrowser;
TopWebBrowser: IWebBrowser;
Document: OleVariant;
WindowName: string;
begin
CurWebrowser := pDisp as IWebBrowser;
TopWebBrowser := (Sender as TWebBrowser).DefaultInterface;
if CurWebrowser = TopWebBrowser then
Memo1.Lines.Add('Complete document was loaded')
else;
begin
Document := CurWebrowser.Document;
WindowName := Document.ParentWindow.Name;
Memo1.Lines.Add(Format('Frame "%s" was loaded', [WindowName]));
end;
end;
TWebBrowser에서 특정문자를 부각시키고 위치하기
TWebBrowser에 불려진 윕분서에서 특정 문자열을 찾아서 부각시키고
찾은 문자열에 화면을 스크롤하여 보여줍니다.
아래의 코드는 찾은 문자열의 배경을 빨간색으로 바꾸어 줍니다.
prefix 를 바꾸어주면 다르게도 가능하겠죠. ^^
TEmbeddedWB에서도 WBLocateHighlight(WB: TWebBrowser; Text: string)을
WBLocateHighlight(WB: TEmbeddedWB; Text: string)으로 바꾸어주면 사용 할 수 있습니다.
uses mshtml;
procedure WBLocateHighlight(WB: TWebBrowser; Text: string) ;
const
prefix = '<span style="color:white; background-color: red;">';
suffix = '</span>';
var
tr: IHTMLTxtRange;
begin
if Assigned(WB.Document) then
begin
tr := ((wb.Document AS IHTMLDocument2).body AS IHTMLBodyElement).createTextRange;
while tr.findText(Text, 1, 0) do
begin
tr.pasteHTML(prefix + tr.htmlText + suffix) ;
tr.scrollIntoView(True) ;
end;
end;
end;
사용 방법은
WBLocateHighlight(WebBrowser1,'musk95') ;
입니다.
WebBrowser를 이용하여 폼 필드에 값 할당과 submit
i) 직접 접근 방법
WebBrowser1.OleObject.Document.frmMain.userID.value := 'k133';
// 폼 이름과 폼 필드 이름을 직접 코딩
WebBrowser1.OleObject.Document.frmMain.submit;
=> "구성원이 없습니다." 라는 에러 발생합니다.
ii) 이름으로 접근 방법
WebBrowser1.OleObject.Document.all.item('userID').value := 'k133';
WebBrowser1.OleObject.Document.all.item('submit').Click;
빈화면을 만들기
procedure NewDocument;
var
C: array[0..MAX_PATH-1] of Char;
FileName: String;
begin
GetTempPath( MAX_PATH, C );
FileName := C;
if FileName[ Length( FileName ) ] <> '\' then FileName := FileName + '\';
FileName := FileName + 'test.htm';
with TFileStream.Create( FileName, fmCreate ) do Free;
WebBrowser.Navigate( FileName );
end;
소스보기
procedure 프로시져명;
var MyDocument: OleVariant;
begin
MyDocument := WebBrowser1.Document;
Memo1.Lines.Clear;
// HTML 소스 보기
Memo1.Lines.Add(MyDocument.DocumentElement.InnerHTML);
// 일반 내용 보기
Memo1.Lines.Add(WebBrowser1.OleObject.Document.DocumentElement.InnerText);
Memo1.Visible:= True;
Memo1.Align:= alClient;
if Memo1.CanFocus then
Memo1.SetFocus;
end;
싸이트이동
WebBrowser1.Navigate('싸이트명');
WebBrowser1.Navigate('About:Blank'); //웹브라우져초기화
현재오픈된 URL가지고 오기
procedureOKGetURL(AStrings:TStrings);
functionWebBrowserCheck(constWebb:IWebBrowser2):Boolean;
var WebV:Variant;
Buf:string;
begin
WebV:=Webb;
Result:=False;
if Assigned(Webb)then
try
Buf:=WebV.Document.URL;
Result:=True;
except;
end;
end;
var
Count,i:integer;
mShellWindow:IShellWindows;
Webb:IWebBrowser2;
WebV:Variant;
begin
mShellWindow:=CreateComObject(CLASS_ShellWindows) as IShellWindows;
Count:=mShellWindow.Count;
fori:=0 to Count-1 do
begin
ifWebBrowserCheck(mShellWindow.Item(i)asIWebBrowser2) then
begin
try
Webb:=IWebBrowser2(mShellWindow.Item(i));
WebV:=Webb;
AStrings.Add(WebV.Document.URL);
except;
end;
end;
end;
end;
WebBrowser에서 이미지 찾아서 클릭하기
uses
MSHTML;
var
iDoc: IHtmlDocument2;
i: integer;
ov: OleVariant;
iDisp: IDispatch;
iColl: IHTMLElementCollection;
InputImage: HTMLInputImage;
begin
WebBrowser1.ControlInterface.Document.QueryInterface(IHtmlDocument2, iDoc);
if not Assigned(iDoc) then
begin
Exit;
end;
ov := 'INPUT';
iDisp := iDoc.all.tags(ov);
if Assigned(IDisp) then
begin
IDisp.QueryInterface(IHTMLElementCollection, iColl);
if Assigned(iColl) then
begin
for i := 1 to iColl.Get_length do
begin
iDisp := iColl.item(pred(i), 0);
iDisp.QueryInterface(HTMLInputImage, InputImage);
if Assigned(InputImage) then
begin
if InputImage.Name = 'submit' then
// if the name is submit / falls der name submit lautet
begin
InputImage.Click; // click it / klick es
end;
end;
end;
end;
end;
end;
// 2.
procedure TForm1.Button1Click(Sender: TObject);
var
i: Word;
Document: IHtmlDocument2;
str: string;
begin
// Schleife uber alle Bilder im Webbrowser
for i := 0 to WebBrowser1.OleObject.Document.Images.Length - 1 do
begin
Document := WebBrowser1.Document as IHtmlDocument2;
// URL auslesen
Str := (Document.Images.Item(i, 0) as IHTMLImgElement).Href;
// Dateiname des Bildes uberprufen
if Pos('submit_icon.gif', str) <> 0 then
begin
((Document.Images.Item(i, 0) as IHTMLImgElement) as IHTMLElement).Click;
end;
end;
end;
'Delphi Tip > 인터넷' 카테고리의 다른 글
indy idhttp 이용해서 파일 업로드 (0) | 2021.11.19 |
---|---|
웹페이지 FillInForm (0) | 2021.11.18 |
웹브라저를 통한 사이트HTML 옵션문 추가방법 (0) | 2021.11.17 |
네트워크(인터넷) 연결 유무 (물리적) (0) | 2021.11.15 |
IdHTTP 이용하여 파일 다운로드 (0) | 2021.11.12 |
댓글