우선 ActiveX 관련 프로젝트를 만들어야 함.
- BDS에서 File > New > Other 를 선택하여 New Items 창을 불러낸 후, Delphi Projects > ActiveX Library 를 선택하여 프로젝트를 생성. (프로젝트의 이름을 적당하게 짓자)
- 새로 만들어진 프로젝트에 COM Object 를 추가해야 함. 다시 File > New > Other 를 선택하고, COM Object 를 선택하자.
- COM Object Wizard 가 뜬다. Class Name 을 적당히, 나머지는 손댈 필요 없고 Description(설명글) 도 적당히, Options 에서 Include Type Library 는 체크를 해제한다. 그리고 OK.
- 새로 만들어진 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
'Delphi' 카테고리의 다른 글
[Delphi] 디렉토리 선택 Dialog 띄우기 (0) | 2009.04.18 |
---|---|
[Delphi] 동적으로 COM Server 등록하기 (DLL, OCX) (0) | 2009.04.18 |
[Delphi] DeleteFile 함수 사용시 읽기전용파일이 지워지지 않을 때 (0) | 2009.04.18 |
[Delphi] 유니코드인지 판별하는 법 (텍스트 파일) (0) | 2009.04.18 |
[Delphi] 유니코드 파일 쓰기 (0) | 2009.04.18 |