{*******************************************************} { } { BS1ExtIconAip } { } { Copyright (C) 2025 kku } { } {*******************************************************} unit BS1ExtIconAip; interface uses Winapi.Windows, Winapi.ActiveX, System.Win.ComServ, System.Win.ComObj, Winapi.ShlObj, System.SysUtils; const EXTICON_GUID = '{05F4DC17-689D-467F-AA36-08F52942D30A}'; type TBS1ExtIconAip = class(TComObject, IExtractIcon, IPersistFile) private FFilePath: String; // 파일 경로 FIconPath: String; // 아이콘 경로 FIconIndex: Integer; // 아이콘 인덱스 public Constructor Create; // IExtractIcon 메서드 { 출처 : https://blog.codingcat.kr/116 GetIconLocation(...) uFlags 쉘 익스텐션의 작동을 바꿀 수 있는 옵션들입니다. GIL_ASYNC는 아이콘 추출 작업이 오래 걸릴 것인지 물어보기 위해 전달되는 플래그(flag)로, 만일 오래 걸린다면 쉘 익스텐션은 Windows 탐색기에게 아이콘 추출 작업을 백 그라운드에서 수행해 줄 것을 요청할 수 있습니다. 이렇게 하면 Windows 탐색기 창 자체가 버벅거리는 일이 없습니다. 다른 플래그(flag)인 GIL_FORSHELL 및 GIL_OPENICON은 네임스페이스 확장에서 의미 있습니다. 우리가 작업할 프로젝트에서는 실행 시간이 오래 걸리는 코드가 없으므로, 이러한 플래그(flag)들을 고려하지 않아도 됩니다. szIconFile, cchMax szIconFile은 쉘(shell)이 제공하는 버퍼로서 우리는 사용하고자 하는 아이콘이 포함된 파일의 이름을 이 곳에 보관하면 됩니다. cchMax는 쉘(shell)이 제공한 버퍼의 크기로서, 단위는 문자 수 입니다. piIndex 우리가 사용하고자 하는 아이콘이 szIconFile에서 몇 번째 인덱스에 해당하는지를 지정할 정수에 대한 포인터입니다(번역자 주: 이것도 쉘(shell)이 제공하는 변수를 가리키는 유효한 포인터입니다). pwFlags 쉘(shell)이 제공한 UINT형 변수를 가리키는 유효한 포인터로서, 반환하는 플래그(flag)에 따라 Windows 탐색기의 작동을 우리가 변경할 수 있습니다. 이 곳에 들어갈 수 있는 플래그의 종류에 대하여는 잠시 후 설명하겠습니다. GetIconLocation 메소드는 szIconFile과 piIndex 매개 변수(parameter)에 내용을 채운 다음 S_OK를 반환합니다. 우리가 커스텀 아이콘을 제공하지 않기로 결정하였다면 S_FALSE를 반환합니다. 이 때 Windows 탐색기는 일반적인 아이콘(알 수 없는 파일)으로 표시해 줄 것입니다. pwFlags를 통해 반환할 수 있는 플래그(flag)들에는 다음과 같은 종류가 있습니다. GIL_DONTCACHE Windows 탐색기가 szIconFile/piIndex로 지정된 파일이 최근에 사용되었는지 여부를 확인하기 위해 캐시를 검색하는 것을 못하게 합니다. 그 결과 IExtractIcon::Extract 메소드가 항상 호출됩니다. ‘두 번째 아이콘 추출 방법’에서 이 플래그에 대해 많은 이야기를 하겠습니다. GIL_NOTFILENAME MSDN에 따르면 GetIconLocation이 값을 반환할 때 이 플래그가 있으면 Windows 탐색기가 szIconFile/piIndex를 무시한다고 합니다. 명백하기도 이 플래그(flag)는 쉘 익스텐션이 어떻게 Windows 탐색기로 하여금 IExtractIcon::Extract를 호출하게 만드는지를 보여주는 플래그임이 확실한데, 실제로는 GetIconLocation이 값을 반환할 때 이 플래그는 Windows 탐색기의 작동에 아무 영향을 미치지 못합니다. 이와 관련해서는 나중에 설명하겠습니다. GIL_SIMULATEDOC 이 플래그(flag)는 Windows 탐색기가 쉘 익스텐션에서 반환된 아이콘을 가져가서, “한 쪽 귀퉁이가 접힌 종이” 모양의 아이콘과 합친 다음에, 그 합성된 아이콘을 해당 파일에 적용해서 보여주라는 의미를 갖습니다. 필자는 잠시 후 이 플래그의 결과를 보여드리겠습니다. } function GetIconLocation(uFlags: UINT; szIconFile: PChar; cchMax: UINT; out piIndex: Integer; out pwFlags: UINT): HResult; stdcall; function Extract(pszFile: PWideChar; nIconIndex: UINT; out phiconLarge, phiconSmall: HICON; nIconSize: UINT): HResult; stdcall; // IPersistFile 메서드 function GetClassID(out classID: TCLSID): HResult; stdcall; function IsDirty: HResult; stdcall; function Load(pszFileName: PWideChar; dwMode: Longint): HResult; stdcall; function Save(pszFileName: PWideChar; fRemember: BOOL): HResult; stdcall; function SaveCompleted(pszFileName: PWideChar): HResult; stdcall; function GetCurFile(out pszFileName: PWideChar): HResult; stdcall; end; TBS1ExtIconAipFac = class(TComObjectFactory) public procedure UpdateRegistry(bRegister: Boolean); override; end; implementation uses System.Win.Registry, Tocsg.AIP, Tocsg.Registry, Vcl.Graphics, Tocsg.Path; const CLASS_BS1ExtIconAip: TGUID = EXTICON_GUID; //var // _bInit: Boolean = false; { TBS1ExtIconAip } Constructor TBS1ExtIconAip.Create; begin inherited Create; FIconPath := 'C:\taskToCSG\eCrmHE\Images\22_0530 favicon\favicon_HE_보안.ico'; FIconIndex := 0; // 기본 아이콘 인덱스 end; (* .pptx 핸들러 등록하고 싶을 경우 1. HKEY_CLASSES_ROOT\.pptx 가 있는지 찾는데 (보통 MS 파일은 있다) 2. HKEY_CLASSES_ROOT\.pptx 기본 값 확인 (PowerPoint.Show.12) 2. HKEY_CLASSES_ROOT\PowerPoint.Show.12\ShellEx\IconHandler 키에 {05F4DC17-689D-467F-AA36-08F52942D30A} 값을 등록해준다. 3. dll을 등록 4. dll 해제 시 ShellEx\IconHandler 키 복구 또는 삭제 PDF의 경우 HKEY_CLASSES_ROOT\Acrobat.Document.DC\Shellex\IconHandler 이게 우선처리됨... *) // IExtractIcon.GetIconLocation function TBS1ExtIconAip.GetIconLocation(uFlags: UINT; szIconFile: PChar; cchMax: UINT; out piIndex: Integer; out pwFlags: UINT): HResult; var sExt, sTempDir: String; bIsEnc: Boolean; begin // 특정 조건에 따라 아이콘 경로 변경 // if SameText(ExtractFileExt(FFilePath), '.special') then // begin // FIconPath := 'C:\Path\To\SpecialIcon.ico'; // FIconIndex := 0; // end // else // begin // FIconPath := 'C:\Path\To\DefaultIcon.ico'; // FIconIndex := 0; // end; // if not _bInit then // begin // CoInitialize(nil); // _bInit := true; // SetRegValueString(HKEY_CURRENT_USER, 'Software\eCrmHomeEdition', 'BLog0-CoInitialize', FFilePath, true); // end; SetRegValueString(HKEY_CURRENT_USER, 'Software\eCrmHomeEdition', 'BLog1', FFilePath, true); bIsEnc := false; sExt := GetFileExt(FFilePath).ToUpper; sTempDir := 'C:\ProgramData\HE\'; if not FFilePath.ToUpper.StartsWith(sTempDir.ToUpper) then begin if sExt = 'PDF' then begin SetRegValueString(HKEY_CURRENT_USER, 'Software\eCrmHomeEdition', 'BLog2', FFilePath, true); bIsEnc := IsAipEncrytedPDF(FFilePath); if bIsEnc then SetRegValueString(HKEY_CURRENT_USER, 'Software\eCrmHomeEdition', 'BLog3', 'PDF Enc', true) else SetRegValueString(HKEY_CURRENT_USER, 'Software\eCrmHomeEdition', 'BLog3', 'PDF Nor', true); end else if (sExt = 'DOC') or (sExt = 'XLS') or (sExt = 'PPT') or (sExt = 'DOT') or (sExt = 'PPS') or (sExt = 'POT') then begin bIsEnc := IsAipEncrytedOldOfficeDoc(FFilePath, sTempDir, STGM_READ); if not bIsEnc then bIsEnc := IsAipEncrytedOfficeDoc(FFilePath, sTempDir, STGM_READ); end else if (sExt = 'DOCX') or (sExt = 'XLSX') or (sExt = 'PPTX') or (sExt = 'DOCM') or (sExt = 'DOTX') or (sExt = 'XLSM') or (sExt = 'XLSB') then begin bIsEnc := IsAipEncrytedOfficeDoc(FFilePath, sTempDir, STGM_READ); end; end; SetRegValueString(HKEY_CURRENT_USER, 'Software\eCrmHomeEdition', 'BLog4', FFilePath, true); if bIsEnc then begin FIconPath := 'C:\taskToCSG\eCrmHE\Images\22_0530 favicon\favicon_HE_보안.ico'; piIndex := FIconIndex; end else begin // FIconPath := 'C:\taskToCSG\eCrmHE\Images\22_0530 favicon\favicon_HE_수면.ico'; if sExt = 'PPTX' then begin FIconPath := 'C:\Program Files (x86)\Microsoft Office\Root\VFS\Windows\Installer\{90160000-000F-0000-0000-0000000FF1CE}\pptico.exe'; piIndex := 10; end else if sExt = 'PDF' then begin // FIConPath := 'C:\Program Files (x86)\UPDF\defaulticon.ico'; // FIconPath := 'C:\taskToCSG\eCrmHE\Images\22_0530 favicon\favicon_HE_수면.ico'; FIconPath := 'C:\WINDOWS\Installer\{AC76BA86-1042-1033-7760-BC15014EA700}\_PDFFile.ico'; piIndex := 0; end; end; // SetRegValueString(HKEY_CURRENT_USER, 'Software\eCrmHomeEdition', 'BLog2', '', true); // 아이콘 경로와 인덱스 반환 StrPLCopy(szIconFile, FIconPath, Length(FIconPath)); // SetRegValueString(HKEY_CURRENT_USER, 'Software\eCrmHomeEdition', 'B1', szIconFile, true); // SetRegValueString(HKEY_CURRENT_USER, 'Software\eCrmHomeEdition', 'B2', FIconPath, true); pwFlags := 0; // GIL_PERINSTANCE; // 파일별로 고유 아이콘 제공 Result := S_OK; // else // Result := S_FALSE; end; // IExtractIcon.Extract function TBS1ExtIconAip.Extract(pszFile: PWideChar; nIconIndex: UINT; out phiconLarge, phiconSmall: HICON; nIconSize: UINT): HResult; var ic: TIcon; begin // 지정된 아이콘 로드 // ic := TIcon.Create; // ic.LoadFromFile(FIconPath); // phiconLarge := ic.Handle; // LoadImage(HInstance, PWideChar(FIconPath), FIconIndex); // phiconSmall := ic.Handle; // ExtractIcon(HInstance, PWideChar(FIconPath), FIconIndex); //// ic.Free; // // if (phiconLarge <> 0) and (phiconSmall <> 0) then // Result := S_OK // else Result := S_FALSE; end; // IPersistFile.GetClassID function TBS1ExtIconAip.GetClassID(out classID: TCLSID): HResult; begin classID := CLASS_BS1ExtIconAip; Result := S_OK; end; // IPersistFile.IsDirty function TBS1ExtIconAip.IsDirty: HResult; begin Result := S_FALSE; // 항상 깨끗한 상태 end; // IPersistFile.Load function TBS1ExtIconAip.Load(pszFileName: PWideChar; dwMode: Longint): HResult; begin FFilePath := pszFileName; // 파일 경로 저장 Result := S_OK; end; // IPersistFile.Save function TBS1ExtIconAip.Save(pszFileName: PWideChar; fRemember: BOOL): HResult; begin Result := E_NOTIMPL; // 저장은 구현하지 않음 end; // IPersistFile.SaveCompleted function TBS1ExtIconAip.SaveCompleted(pszFileName: PWideChar): HResult; begin Result := E_NOTIMPL; end; // IPersistFile.GetCurFile function TBS1ExtIconAip.GetCurFile(out pszFileName: PWideChar): HResult; begin Result := E_NOTIMPL; end; { TBS1ExtIconAipFac } procedure TBS1ExtIconAipFac.UpdateRegistry(bRegister: Boolean); begin if bRegister then begin inherited UpdateRegistry(bRegister); SetRegValueString(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', EXTICON_GUID, 'BSOne ExtIcon', true); end else begin DelRegValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', EXTICON_GUID); inherited UpdateRegistry(bRegister); end; end; initialization // COM 객체 등록 TBS1ExtIconAipFac.Create(ComServer, TBS1ExtIconAip, CLASS_BS1ExtIconAip, 'BS1ExtIconAip', 'BSOne-ExtIcon', ciMultiInstance, tmApartment); end.