2011년 3월 3일 목요일

MS Word 파일에서 텍스트 추출

usesWindows, SysUtils, Variants, Forms, ComObj, OleServer, WordXP;

...
...
...



functionGetWordText(const fName: String): String;
var  WordApp, WordDoc: OleVariant;
  i: Integer;

begin
 // 워드 어플리케이션 생성
  try
    WordApp:=CreateOleObject('Word.Application');
 except
    MessageBox(0,'MS-Word 가 설치되어 있지 않습니다.','Error',MB_OK);
    Exit;
  end;

  // 파일존재 여부 검사  if notFileExists(fName) then begin
    MessageBox(0,'파일이 없습니다.','Error',MB_OK);
    Exit;
  end;

  // 워드 Document Open
  WordDoc:=WordApp.Documents.Open(OleVariant(fName),EmptyParam,EmptyParam,
                                  EmptyParam,EmptyParam,EmptyParam,EmptyParam,
                                  EmptyParam,EmptyParam,EmptyParam);

  // 텍스트 추출
  fori:=1to WordDoc.Sentences.Count do begin    Result:=Result+#13#10+WordDoc.Sentences.Item(i).Text;
    Application.ProcessMessages;
  end;

  // 해제  WordDoc.Close(wdDoNotSaveChanges, EmptyParam, EmptyParam);
  WordApp.Quit(EmptyParam, EmptyParam, EmptyParam);
  WordDoc:=UnAssigned;
  WordApp:=UnAssigned;
end;
 

유니코드로 인코딩 된 텍스트 파일 읽기

텍스트 파일 경로를 넣으면 텍스트내용이 반환.

functionReadUnicode(fName: String): WideString;
var
  F: File;
  FileSize, ReadSize: Cardinal;
  SearchRec: TSearchRec;
  Buffer: WideString;

begin
  FileSize:=0;
  ifFindFirst(fName, faAnyFile, SearchRec)=0then begin    FileSize:=SearchRec.Size;
    FindClose(SearchRec);
  end;

  AssignFile(F, fName);
  SetLength(Buffer, FileSize);
  Reset(F, 1);
  BlockRead(F, PAnsiChar(Buffer)^, FileSize, ReadSize);

  Result:=Buffer;
  CloseFile(F);
end;


사용예
procedureTForm1.Button1Click(Sender: TObject);
const파일경로 = 'C:\유니코드파일.dat';
var sList: TStringList;
begin
  sList:=TStringList.Create;
  try
    sList.Text:=ReadUnicode(파일경로);
    Memo1.Text:=sList.Text;
  finally
    FreeAndNil(sList);
  end;
end;

공유 네트워크 상의 PC에 실행파일 바로 생성하기

1. 테스트를 수행하고자 하는 원격PC에 공유폴더 생성. (쓰기 권한도 설정되어 있어야 함)
 
2. Project Options (Ctrl + Shift + F11) > Directories/Conditionals > Output directory 에서 네트워크 공유 된 디렉토리를 설정.
 
3. 컴파일.
 
4. 원격PC에 실행파일이 생성되어 있음.
 
 
* Project Options 의 Build Configuration 에 Remote 같은 걸 만들어서 저장해 두면 좋을 듯.
네이트온이나 공유폴더에 실행파일을 복사해서 옮기는 것 보다 더 빠르고 편하다고 생각함.
당연한 원리로 DCU 파일도 원격지의 어떤 한 곳에 몰아둘 수도 있음. Path 설정을 따로 해야하지만.
라이브러리도 한 곳에 모아놓고 각 개발PC에서 땡겨써도 편할 것 같고.
 
쓸만한 원격지원 프로그램을 이용한다면 제자리에 앉아서 모든 테스트PC를 컨트롤할 수 있지 않을까 생각함.
뱃살은 더 늘어나겠지만.
 
 
ps. 실행파일을 원격PC와 로컬에 동시에 생성할 수는 없을까 해서 이리저리 해봤지만 한 번에 하나만 생성된다는 결론을 내렸음.
옵션의 제목이 Output Directory지 Output Directories가 아니지 않은가.

프로세스가 살아있는지 검사, 프로세스 죽이기

프로세스가 살아있는지 검사
 
// uses 에 TlHelp32 추가
functionIsRunningProcess(constProcName: String) : Boolean;
var  Process32: TProcessEntry32;
  SHandle:   THandle;
  Next:      Boolean;

begin
  Result:=False;
 
  Process32.dwSize:=SizeOf(TProcessEntry32);
  SHandle         :=CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS,0);
 
  // 프로세스 리스트를 돌면서 매개변수로 받은 이름과 같은 프로세스가 있을 경우 True를 반환하고 루프종료 ifProcess32First(SHandle, Process32) then begin
    repeat
      Next:=Process32Next(SHandle, Process32);
      if AnsiCompareText(Process32.szExeFile, Trim(ProcName))=0then begin
        Result:=True;
        break;
     end;
    until notNext;
  end;
  CloseHandle(SHandle);
end;
 
 
// 사용예.  계산기(calc.exe)가 떠있는지 확인하고 싶을 경우
procedureTForm1.Button1Click(Sender: TObject);
begin
  ifIsRunningProcess('calc.exe') thenOutputDebugString('계산기 있음')
  else                                 OutputDebugString('계산기 없음');
end;
 
 
 
 
 
 
프로세스 죽이기 (강제종료)
 
// uses 에 TlHelp32 추가
functionKillProcess(constProcName: String): Boolean;
var
  Process32: TProcessEntry32;
  SHandle:   THandle;
  Next:      Boolean;
  hProcess: THandle;
  i: Integer;
 
begin
  Result:=True;
 
  Process32.dwSize       :=SizeOf(TProcessEntry32);
  Process32.th32ProcessID:=0;
  SHandle                :=CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 
  // 종료하고자 하는 프로세스가 실행중인지 확인하는 의미와 함께...
  ifProcess32First(SHandle, Process32) then begin
    repeat
      Next:=Process32Next(SHandle, Process32);
      ifAnsiCompareText(Process32.szExeFile, Trim(ProcName))=0thenbreak;
    until notNext;
  end;
  CloseHandle(SHandle);
 
  // 프로세스가 실행중이라면 Open & Terminate  ifProcess32.th32ProcessID<>0then begin    hProcess:=OpenProcess(PROCESS_TERMINATE, True, Process32.th32ProcessID);
    ifhProcess<>0then begin
      if not
TerminateProcess(hProcess, 0) thenResult:=False;
    end    // 프로세스 열기 실패    elseResult:=False;
 
    CloseHandle(hProcess);
  end// if Process32.th32ProcessID<>0
  elseResult:=False;
end;
 
 
 
 
// 사용예. 계산기(calc.exe) 프로세스를 죽이고자 할 때.
procedureTForm1.Button1Click(Sender: TObject);
begin
  ifKillProcess('calc.exe') thenOutputDebugString('계산기 죽이기 실패. 계산기 프로세스가 없었거나...')
  else                            OutputDebugString('계산기 죽이기 성공.');
end;

HTML 페이지의 태그 사이를 유랑하기

* 폼에 TWebBrowser 가 하나 박혀 있고, 그 이름은 WebBrowser1 이라고 가정함.
 
 
HTML 페이지의 Form Element 전부 뒤지기
 
procedureTForm1.Button3Click(Sender: TObject);
const  URL = 'http://www.daum.net'/;
 
var
  Document: IHTMLDocument2;
  Form: IHTMLFormElement;
  ovElements: OleVariant;
  i, j: Integer;
 
  // 디버깅용 내부함수. 실제로 사용할 땐 외부에서 정의하고 사용하지만 여기서만...
  procedureD(s: String);
  begin
    OutputDebugString(PChar(s));
  end;
  // ___________________________________________________________
 
begin
  // 웹페이지 로딩
  WebBrowser1.Navigate(URL);
  whileWebBrowser1.readyState<>READYSTATE_COMPLETE doApplication.ProcessMessages;
  Document:=WebBrowser1.Document asIHTMLDocument2;

  ifDocument<>nil then begin   // Document.Forms 모두 뒤지기    fori:=0toDocument.forms.Length-1do begin      // 각 Form 의 Element 모두 뒤지기      Form:=IDispatch(Document.Forms.Item(i, 0)) asIHTMLFormElement;
      ovElements:=Form.elements;
      forj:=0 toovElements.Length-1do begin
        try

          // Form.name & Element name, type, value 출력          D(Form.name+' -> '+ovElements.item(j).Name+' ['+ovElements.item(j).type+'] = '+ovElements.item(j).value);
        except on E:Exception do          D('[Error] '+E.Message);
        end;
      end; // for j    end; // for i  end
  else
D('Document=nil');
end;
 
 
 
 
 
 
 
 
응용편 - 피파온라인2 로그인하기 (피망)
ID, 비번 입력은 OleVariant 를 이용했고,
로그인 버튼 클릭, 로그인 확인은 IHTMLDocument2, IHTMLElementCollection, IHTMLElement 를 이용.
주의: 화면 상에 TWebBrowser 가 그려지고 있어야 함. ID 넣는 부분이 화면 밖으로 나가 있을 경우 로직은 통하지 않음.
 
procedureTForm1.Button4Click(Sender: TObject);
const
  URL = 'http://fifaonline.pmang.com'/;
  ID = '사용ID';
  PW = '사용비번';
  INPUT_NAME_ID = 'usrid';
  INPUT_NAME_PW = 'passwd';
 
var  Document: IHTMLDocument2;
  elementcol: IHTMLElementCollection;
  element: IHTMLElement;
  Input: OleVariant;
  i: Integer;
  LoginComplete: Boolean;
 
begin
  // 웹페이지 로딩
  WebBrowser1.Navigate(URL);
  whileWebBrowser1.readyState<>READYSTATE_COMPLETE doApplication.ProcessMessages;
 
  // ID, 비밀번호 입력
  Input:=WebBrowser1.OleObject.Document.Body.getElementsByTagName('input');
  fori:=0toInput.Length-1do begin
    // ID    if(Input.Item(i).NAME=INPUT_NAME_ID) thenInput.Item(i).value:=ID;
    // 비밀번호    if (Input.Item(i).NAME=INPUT_NAME_PW)thenInput.Item(i).value:=PW;
  end;
 
  // 로그인 버튼 클릭
  Document:=WebBrowser1.Document asIHTMLDocument2;
  Document.all.tags('input').QueryInterface(IHTMLElementCollection, elementcol);
  fori:=0to elementcol.length-1do begin
    element:=elementcol.item(i,0) asIHTMLElement;
    ifSystem.Pos('btn_login', element.outerHTML)>0thenelement.click;
 end;
 
  // 로그인 완료될 때까지 대기 - 로그아웃 버튼이 있으면 로그인 완료 판단  LoginComplete:=False;
  while notLoginComplete do begin    Document.all.tags('img').QueryInterface(IHTMLElementCollection, elementcol);
    for i:=0toelementcol.length-1do begin      element:=elementcol.item(i, 0) asIHTMLElement;
      ifPos('btn_logout', element.outerHTML)>0then begin
        LoginComplete:=True;
        break;
      end; // if
    end; // for
    Sleep(500);
    Application.ProcessMessages;
  end; // while
end;
 

쉐어웨어 프로그램을 만들때 유용한 기법

// 처음 실행될때 글로바 아톰을 선언하고 종료시 이를 제거하지 않습니다.
// 때문에 또 실행하면 당연히 아톰이 선언된채 있기 때문에 알수 있습니다.
// 주로 프로그램이 두번실행되는것을 막을때 사용됩니다.

procedure TForm1.FormShow (Sender: TObject);
var
atom : integer;
begin
if GlobalFindAtom ('오늘비가와~') = 0 then
atom := GlobalAddAtom ('오늘비가와~')
else
begin
ShowMessage ('등록하지 않으면 다시 윈도우를 재부팅해야 사용할수 있습니다. 왠만하면 등록하시죠?');
Close ;
end
end;


========================================

프로그램 중복실행을 방지하려고 GlobalAddAtom과 GlobalFindAtom함수를 아래와 같이 썼습니다.
var

atom:Word; //전역변수

procedure TForm1.FormCreate(Sender: TObject);
begin
  if GlobalFindAtom('ISRUNNING') = 0 then
    atom := GlobalAddAtom('ISRUNNING')
  else begin
    MessageDlg('프로그램이 이미 실행중입니다.', mtWarning,[mbOK], 0);
    Halt;
  end;

end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  GlobalDeleteAtom(atom);
end;

==============================

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type
TMainForm = class(TForm)
btnEncode: TButton;
btnDecode: TButton;
procedure btnEncodeClick(Sender: TObject);
procedure btnDecodeClick(Sender: TObject);
private
FAtom: DWORD;
public
function EncodeURL(const URL: string): DWORD;
function DecodeURL(dwAtom: Integer): string;
end;

var
MainForm: TMainForm;

implementation

{$R *.DFM}

function TMainForm.DecodeURL(dwAtom: DWORD): string;
var
Buf: array[0..255] of Char;
begin
GlobalGetAtomName(dwAtom, Buf, SizeOf(Buf));
Result := Buf;
end;

function TMainForm.EncodeURL(const URL: string): DWORD;
begin
Result := GlobalAddAtom(PChar(URL));
end;

procedure TMainForm.btnEncodeClick(Sender: TObject);
begin
FAtom := EncodeURL('http://www.delphi.co.kr');/
end;

procedure TMainForm.btnDecodeClick(Sender: TObject);
begin
ShowMessage(DecodeURL(FAtom));
end;

end.
=====================================================================
procedure TfrmMain.FormShow(Sender: TObject);
var
  atom : integer;
  wdate : string;
  cdate : TDateTime;
  tmp_str : string;
  nAtm :Word;
  buf :Array [0..255] of Char;
begin
  wdate := '2010-12-30';
  FAtom := EncodeURL(wdate);
  //해지
  //GlobalDeleteAtom(FAtom);
  if FAtom = 0 then begin
    atom := GlobalAddAtom (PChar( FAtom ));
  end
  else begin
    tmp_str := DecodeURL(FAtom);
    cdate := StrToDate(tmp_str);
    if TRUNC(cdate) < TRUNC(now()) then begin
      ShowMessage ('사용기간이 지났습니다.');
      Close ;
    end;
  end;
end;

indy10에서 한글깨지는 경우

uses에 IdGlobal, IdContext 추가

채팅의 경우 Acontext....ReadIn(#13, enUTF8)을 하면됨

참고로 Get()에 마우스 올려서 TIdEncoding, enDefault가 인자로 지원한다면 Get('텍스트', enUTF8); 이렇게 하면됨

========================================================================
델마당의 동영상강좌중 메신져 부분을 참고로 만들었다.

unit uClient;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, StdCtrls,
  ExtCtrls, IdAntiFreezeBase, IdAntiFreeze, IdGlobal;
type
  TfmMain = class(TForm)
    Memo1: TMemo;
    Edit1: TEdit;
    Button1: TButton;
    IdTCPClient1: TIdTCPClient;
    Timer1: TTimer;
    Edit2: TEdit;
    Edit3: TEdit;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Edit1KeyPress(Sender: TObject; var Key: Char);
    procedure Timer1Timer(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Edit2KeyPress(Sender: TObject; var Key: Char);
    procedure Edit3KeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  fmMain: TfmMain;
implementation
{$R *.dfm}
procedure TfmMain.Button1Click(Sender: TObject);begin
  try
    if Not IdTCPClient1.Connected then
    begin
      IdTCPClient1.Connect;
      fmMain.Color := clLime;
      Caption := '클라이언트 프로그램' + ' 동작';
      IdTCPClient1.IOHandler.WriteLn(Format('Login;%s;',[Edit2.Text]),enUTF8);
    end
    else
    begin
      IdTCPClient1.Disconnect;
      fmMain.Color := clRed;
      Caption := '클라이언트 프로그램' + ' 멈춤';
    end;
  except on E: Exception do
    ShowMessage('서버와 접속할 수 없습니다.');
  end;
end;
procedure TfmMain.Button2Click(Sender: TObject);begin
  IdTCPClient1.IOHandler.WriteLn(Format('Msg;%s;%s;%s;',[Edit2.Text, Edit3.Text, Edit1.Text]),enUTF8);
end;
procedure TfmMain.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then Begin
//    Key := #0;
    IdTCPClient1.IOHandler.WriteLn(Format('Talk;%s;%s;',[Edit2.Text, Edit1.Text]), enUTF8);
    Edit1.Text := '';
  End;
end;
procedure TfmMain.Edit2KeyPress(Sender: TObject; var Key: Char);begin
  if Key = #$0D then
    Button1Click(Button1);
end;
procedure TfmMain.Edit3KeyPress(Sender: TObject; var Key: Char);begin
  if Key = #$0D then
    Button2Click(Button2);
end;
procedure TfmMain.Timer1Timer(Sender: TObject);var
  stTemp: String;
begin
  if Not IdTCPClient1.Connected then Exit;
    stTemp := IdTCPClient1.IOHandler.ReadLn(#10, 5, 0,enUTF8); //2009는 유니코드사용.
  if stTemp <> '' then
    Memo1.Lines.Add(stTemp);
end;
end.

인디는 엔터를 치고 전송하면 #$0D#$0A를 붙여서 전송한다.