본문 바로가기
Delphi Tip/인터넷

Delphi WebBrowser

by MonoSoft 2021. 11. 16.
728x90
반응형

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;

 

728x90
반응형

댓글