우선 ActiveX 관련 프로젝트를 만들어야 함.

 

 

  1. BDS에서 File > New > Other 를 선택하여 New Items 창을 불러낸 후, Delphi Projects > ActiveX Library 를 선택하여 프로젝트를 생성. (프로젝트의 이름을 적당하게 짓자)
  2. 새로 만들어진 프로젝트에 COM Object 를 추가해야 함. 다시 File > New > Other 를 선택하고, COM Object 를 선택하자.
  3. COM Object Wizard 가 뜬다. Class Name 을 적당히, 나머지는 손댈 필요 없고 Description(설명글) 도 적당히, Options 에서 Include Type Library 는 체크를 해제한다. 그리고 OK.
  4. 새로 만들어진 COM Object 파일의 이름을 또 적당히(-_-) 설정하고 본격적인 코딩에 들어감.

 

 

  • uses 에 일단 ComServ, ShlObj, SysUtils, Registry, ShellApi 등을 추가. 

 

선언부 예제

 

type 

// 인터페이스 선언부 

TCTX = class(TComObject, IShellExtInit, IContextMenu) // 3개가 필요함 

private 

FSelectedFile: array [0..MAX_PATH] of Char; 

protected 

{ IShellExtInit } 

function IShellExtInit.Initialize = ShellInitialize; // 이름을 오버로딩해야함 

// 이게 오버로딩된 함수 

function ShellInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject; 

                         hKeyProgID: HKEY); HResult; stdcall

{ IContextMenu } 

function QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall

function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall

function GetCommandString(idCmd, uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult; stdcall

end

  

  

// 인터페이스는 인스턴스가 없으므로 팩토리를 통해 인스턴스를 획득  

TCTX_Factory = class(TComObjectFactory) 

public 

procedure UpdateRegistry(Register: Boolean); override

end

 

 

 

GUID가 필요하다. 대충 이런식으로 선언이 되어있을 것이다.

만약 수동적으로 GUID가 필요하다면 BDS 소스에디터에서 Ctrl + Shift + G 를 누르면 생성해준다.

  

const 

Class_CTX: TGUID = '{B10F5F91-4632-4ED2-A841-7DFBB02D77E6}'

 

 

 

 

구현부 예제

  

{ TCTX } 

// 선택된 파일명을 받아오는 부분. Drag&Drop 형태로 받아온다는 걸 이해하자. 

fucntion TCTX.ShellInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject; hKeyProgID: HKEY): HResult; 

var 

Medium: TStgMedium; 

FE: TFormatETC; 

begin 

Result:=S_FALSE; 

if lpdobj=nil then Exit; 

    

   // 드롭된 파일명을 풀어냄 

with FE do begin 

cfFormat:=CF_HDROP; 

ptd:=nil

dwAspect:=DVASPECT_CONTENT; 

lIndex:=-1

tymed:=TYMED_HGLOBAL; 

end

  

if Failed(lpdobj.GetData(FE, Medium)) then Exit; 

  

try 

// 선택된 파일이 한개일 경우 = $FFFFFFFF 

if DragQueryFile(Medium.hGlobal, $FFFFFFFF, nil, 0)=1 then begin 

// 첫번째 파일 = 0 (두번째 파일 = 1 ...) 

DragQueryFile(Medium.hGlobal, 0, FSelectedFile, MAX_PATH); 

Result:=S__OK; 

end

finally 

ReleaseStgMedium(Medium); 

end

end

  

  

  

// 탐색기 -> Query (to this App) : 메뉴에 추가시킬게 있으면 추가하라 

function TCTX.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; 

begin 

Result:=S_OK; 

  

// uFlags=CMF_DEFAULTONLU -> 더블클릭 제외 (일반적 실행) 

// FSelectedFile[0]<>#0   -> 선택된 파일이 없는 경우 제외 

if ((uFlags and CMF_DEFAULTONLY)=0) and (FSelectedFile[0]<>#0) then begin

// 디렉토리나 드라이브가 아닌 파일이 넘어온 경우 Exit 

if FileExists(String(FSelectedFile)) then Exit; 

end

  

// 메뉴추가 

// 두개째 추가하려면 idCmdFirst+1, 세개째는 idCmdFirst+2 이런식으로 추가가능 

InsertMenu(Menu, indexMenu, MF_STRING or MF_BYPOSITION, idCmdFirst, PChar('다 귀찮아...')); 

  

// 메뉴 앞에 작은 아이콘 이미지 추가 

SetMenuItemBitmaps(Menu, 0, MF_BYPOSITION, bBitmap, bBitmap); 

  

// 추가한 항목의 개수. 하나만 추가했으므로 Result:=1; 

Result:=1

end

  

  

function GetCommandString(idCmd, uType: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;

begin

if idCmd=0 then begin 

// 커서가 메뉴항목 위로 올라올 때 상태표시줄에 표시할 문자열 요청 

if uType=GCS_HELPTEXT then StrLCopy(pszName, PChar('백업: '+FSelectedFile), cchMax); 

Result:=NOERROR; 

end 

else Result:=E_INVALIDARG; 

end;  

  

  

  

  

  

// 메뉴 실행시 실행 될 부분 

function TCTX.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; 

var 

s: String

exInfo: ShellExecuteInfo; 

begin 

s:=FSelectedFile; 

  

FillChar(exInfo, SizeOf(exInfo), 0);

with exInfo do begin

cbSize:=SizeOf(exInfo); 

fMask:=SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT; 

Wnd:=GetActiveWindow; 

lpVerb:='open'

lpParameters:=PChar(s); 

lpFile:=PAnsiChar(GetRootDrive+'Program Files\다귀찮아\SourceBackup.exe'); 

nShow:=SW_SHOWNORMAL; 

end;  

  

ShellExecuteEx(@exInfo); 

  

Result:=S_OK;  

end

  

  

  

  

  

  

  

  

{ TCTX_Factory }  

// 실질적 인스턴스 부분. 레지스트리 등록 

procedure TCTX_Factory.UpdateRegistry(Register: Boolean); 

begin 

if Register then begin 

inherited UpdateRegistry(Register); 

  

CreateRegKey('Directory\shellex\ContextMenuHandlers\CTX','',GUIDToString(Class_CTX)); 

end 

else begin 

DeleteRegKey('Directory\shellex\ContextMenuHandlers\CTX'); 

  

inherited UpdateRegistry(Register); 

end

end

  

  

  

  

  

  

  

// 초기화 부분 

initialization 

// 팩토리 생성 

TComObjectFactory.Create(ComServer, TCTX, Class_CTX, 'CTX', '델파이 소스백업', ciMultiInstance, tmApartment); 

  

  

 

 

 

 

 

전체소스를 컴파일해서 DLL로 만들었다면 이제 그것을 등록할 차례다.

인스톨하는 과정에서 해주어야 하는데 명령어는 아래와 같다.

regsvr32 /s DLL파일 이름(전체경로포함)

 

/s 를 붙인 이유는 등록되었다는 메세지 출력을 보지 않겠다는 뜻이다.

등록된 COM  을 해제하려면 regsvr32 /u DLL파일 이름(전체경로포함) 하면 된다.

 

이제 컨텍스트 메뉴(마우스 오른쪽 클릭으로 나오는) 등록을 하려면 레지스트리에 추가로 등록해줘야 한다.

역시 인스톨 과정에서 해주면 됨.

 

모든 확장명과 연결한다.

[HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\CTX]   마지막 키의 이름은 프로그램의 이름이다.
@="{B10F5F91-4632-4ED2-A841-7DFBB02D77E6}"    기본값이어야 하고 값은 해당 DLL의 GUID여야 한다.

 

 

디렉토리에서 우클릭

[HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers\CTX]  역시 마지막 키의 이름은 프로그램의 이름
@="{B10F5F91-4632-4ED2-A841-7DFBB02D77E6}"   기본값: GUID

 

드라이브명에서 우클릭

[HKEY_CLASSES_ROOT\Drive\shellex\ContextMenuHandlers\CTX]  역시 마지막 키의 이름은 프로그램의 이름
@="{B10F5F91-4632-4ED2-A841-7DFBB02D77E6}"  기본값: GUID

 

 

폴더에서 우클릭

[HKEY_CLASSES_ROOT\Folder\shellex\ContextMenuHandlers\CTX]  마지막 키의 이름은 프로그램의 이름
@="{B10F5F91-4632-4ED2-A841-7DFBB02D77E6}"  기본값: GUID

 

 









Posted by bloodguy
,