BSOne.SFC/Tocsg.Module/MonSecu/ParserLinkFile.pas

739 lines
31 KiB
Plaintext

{*******************************************************}
{ }
{ ParserLinkFile }
{ }
{ Copyright (C) 2022 kku }
{ }
{*******************************************************}
unit ParserLinkFile;
interface
uses
System.SysUtils, System.Classes, Winapi.Windows, System.Generics.Collections;
const
HasLinkTargetIDList = $1; // The shell link is saved with an item ID list (IDList).
HasLinkInfo = $2; // The shell link is saved with link information or without information. The header is available.
HasName = $4; // The shell link is saved with a name string.
HasRelativePath = $8; // The shell link is saved with a relative path string.
HasWorkingDir = $10; // The shell link is saved with a working directory string.
HasArguments = $20; // The shell link is saved with command line arguments.
HasIconLocation = $40; // The shell link is saved with an icon location string.
IsUnicode = $80; // The shell link contains Unicode encoded strings. This bit SHOULD be set.
ForceNoLinkInfo = $100; // The LinkInfo structure is ignored.
HasExpString = $200; // The shell link is saved with an EnvironmentVariableDataBlock
RunInSeparateProcess = $400; // The target is run in a separate virtual machine when launching a link target that is a 16-bit application.
Unused1 = $800; // A bit that is undefined and MUST be ignored.
HasDarwinID = $1000; // The shell link is saved with a DarwinDataBlock
RunAsUser = $2000; // The application is run as a different user when the target of the shell link is activated.
HasExpIcon = $4000; // The shell link is saved with an IconEnvironmentDataBlock
NoPidlAlias = $8000; // The file system location is represented in the shell namespace when the path to an item is parsed into an IDList.
Unused2 = $10000; // A bit that is undefined and MUST be ignored.
RunWithShimLayer = $20000; // The shell link is saved with a ShimDataBlock
ForceNoLinkTrack = $40000; // The TrackerDataBlock is ignored
EnableTargetMetadata = $80000; // The shell link attempts to collect target properties and store them in
// the PropertyStoreDataBlock (section 2.5.7) when the link target is set.
DisableLinkPathTracking = $100000; // The EnvironmentVariableDataBlock is ignored.
DisableKnownFolderTracking = $200000; // The SpecialFolderDataBlock (section 2.5.9) and the KnownFolderDataBlock are ignored when loading the shell link.
DisableKnownFolderAlias = $400000; // If the link has a KnownFolderDataBlock, the unaliased form of the known folder IDList SHOULD be used when
// translating the target IDList at the time that the link is loaded.
AllowLinkToLink = $800000; // Creating a link that references another link is enabled. Otherwise, specifying a link as the target IDList SHOULD NOT be allowed
UnaliasOnSave = $1000000; // When saving a link for which the target IDList is under a known folder, either the unaliased form of that known folder or the target IDList SHOULD be used.
PreferEnvironmentPath = $2000000; // The target IDList SHOULD NOT be stored; instead, the path specified in the EnvironmentVariableDataBlock SHOULD be used to refer to the target.
KeepLocalIDListForUNCTarget = $4000000; // When the target is a UNC name that refers to a location on a local machine, the local path IDList in the PropertyStoreDataBlock
// SHOULD be stored, so it can be used when the link is loaded on the local machine.
Unused3 = $8000000; // A bit that is undefined and MUST be ignored.
Unused4 = $10000000; // A bit that is undefined and MUST be ignored.
Unused5 = $20000000; // A bit that is undefined and MUST be ignored.
Unused6 = $40000000; // A bit that is undefined and MUST be ignored.
Unused7 = $80000000; // A bit that is undefined and MUST be ignored.
VolumeIDAndLocalBasePath = $1;
CommonNetworkRelativeLinkAndPathSuffix = $2;
ValidDevice = $1;
ValidNetType = $2;
type
TLinkFileHeader = packed record
dwHeaderSize: DWORD; // 헤더의 크기로 항상 0x0000004C(76) 값
// arrLinkCLSID: array [0..15] of AnsiChar; // 클래스 식별자(class identifier;CLSID)로 항상 00021401-0000-0000-C000-000000000046 값
CLSID: TGUID; // 클래스 식별자(class identifier;CLSID)로 항상 00021401-0000-0000-C000-000000000046 값
dwLinkFlags, // 링크 대상의 다양한 정보에 대한 플래그
dwFileAttrFlags: DWORD; // 링크 대상의 파일 특성 정보
ftCreate, // 링크 대상의 생성 시간
ftAccess, // 링크 대상의 접근 시간
ftModify: TFileTime; // 링크 대상의 쓰기 시간
dwFileSize, // 링크 대상의 크기
dwIconIndex, // 아이콘 인덱스
dwShowCommand: DWORD; // 링크가 실행될 때 응용프로그램의 동작 모드
wHotKey, // 핫키 정보
wPatch: WORD; // 예약된 필드 (항상 0), Has a 0x00 entry
dwReserved1, // 예약된 필드 (항상 0)
dwReserved2: DWORD; // 예약된 필드 (항상 0)
end;
TLinkFileLocationInfo = packed record
dwLinkInfoSize,
dwLinkInfoHeaderSize,
dwLinkInfoFlags,
dwOffsetVolumeTable,
dwOffsetBasePath,
dwOffsetNetwork,
dwOffsetFinalPath: DWORD;
end;
TLinkFileLocalVolumeTable = packed record
dwVolumeTableLength,
dwVolumeType,
dwVolumeSerialNbr,
dwOffsetVolumeName: DWORD;
end;
TLinkFileNetworkVolumeTable = packed record
dwNetVolumeLength,
dwNetFlags,
dwNetNameOffset,
dwDeviceNameOffset,
dwNetProviderType: DWORD;
end;
PLfiEnt = ^TLfiEnt;
TLfiEntList = TList<PLfiEnt>;
TLfiEnt = record
sCaption,
sValue: String;
ChildList: TLfiEntList;
end;
TParserLinkFile = class(TObject)
private
LfiEntList_: TLfiEntList;
procedure OnEntNotify(Sender: TObject; const Item: PLfiEnt; Action: TCollectionNotification);
function AddLinkFileInfo(pParent: PLfiEnt; sCaption, sValue: String; nInsertIdx: Integer = -1): PLfiEnt;
public
Constructor Create;
Destructor Destroy; override;
function LoadFromStream(aStream: TStream; bClear: Boolean = true; nIncUtcMin: Integer = -1): Boolean;
function LoadFromFile(sPath: String): Boolean;
property LfiEntList: TLfiEntList read LfiEntList_;
end;
function GetLfiValueFromCaption(aList: TLfiEntList; sCaption: String): String;
implementation
uses
System.DateUtils, Tocsg.Convert, Tocsg.DateTime, Tocsg.Safe, Tocsg.Keyboard, Tocsg.Files;
resourcestring
RS_LinkFIleInfo = 'Link 파일 정보';
RS_Name = '이름';
RS_Path = '경로';
RS_CreateDT = '생성일';
RS_AccessDT = '사용일';
RS_ModifyDT = '수정일';
RS_LinkFIleHeaderInfo = 'Link 파일 헤더 정보';
RS_HeaderSize = '헤더 크기';
RS_ExistsIDListInShellHeader = 'ShellLinkHeader에 ID List가 있습니다.';
RS_NoExistsIDListInShellHeader = 'ShellLinkHeader에 ID List가 존재하지 않습니다.';
RS_LinkInfoPresent = 'Link Info가 존재합니다.';
RS_NoLinkInfoAvailable = 'Link Info가 존재하지 않습니다.';
RS_ShellLinkNamePresent = 'Shell link 이름이 존재합니다.';
RS_RelativeShellLinkPathUsed = 'Shell link 경로가 사용됩니다.';
RS_ShellWorkingDirEntered = 'Shell Working Directory가 입력되었습니다.';
RS_CommandLineArgumentsEntered = 'Command line 인수가 입력되었습니다.';
RS_IconLocationEntered = 'Icon 위치가 입력되었습니다.';
RS_LinkContainsUnicode = 'Link에 유니코드가 포함되어 있습니다.';
RS_LinkInfoStructureIgnored = 'Link 정보 구조가 무시됩니다.';
RS_LinkSavedInEnvironmentVariableDataBlock = 'Link는 EnvironmentVariableDataBlock에 저장됩니다.';
RS_RunTargetInVirtualMachine = '가상 컴퓨터에서 대상 실행 (16 비트 응용 프로그램)';
RS_LinkHasDrawinSection = 'Link에 Darwin section이 있습니다.';
RS_LinkRunsAsDifferentUser = 'Link가 다른 사용자로 실행됩니다.';
RS_LinkSavedWithIconEnvironmentDataBlock = 'Link는 IconEnvironmentDataBlock과 함께 저장됩니다.';
RS_FileSystemLocationRepresentedInShellNamespace = '파일 시스템 위치가 shell namespace에 표시됩니다.';
RS_ShellLinkHasShimDataBlock = 'Shell link에 ShimDataBlock이 있습니다.';
RS_TrackerDataBlockIgnored = 'TrackerDataBlock이 무시됩니다.';
RS_ShellLinkCollectsTargetProperties = 'Shell link가 대상 속성을 수집합니다.';
RS_EnvironmentVariableDataBlockIgnored = 'EnvironmentVariableDataBlock이 무시됩니다.';
RS_SpecialFolderDataBlockNKnownFolderDataBlockIgnored = 'SpecialFolderDataBlock 및 KnownFolderDataBlock이 무시됩니다.';
RS_KnownFolderDataBlock4TranslatingTargetIDList = '대상 ID List 변환에 KnownFolderDataBlock를 사용합니다.';
RS_CreatingLinkReferncesAnotherLink = '다른 Link를 참조하는 Link를 만들 수 있습니다.';
RS_UseUnaliased = 'Link를 저장하기 위해 Alias화 되지 않은 형식의 알려진 폴더 또는 대상 ID List를 사용하십시오.';
RS_TargetIDListShouldNotStored = '대상 ID List를 저장해서는 안됩니다 (SHOULD NOT SHOULD NOT).';
RS_StorePropertyStoreDataBlockIfTargetUNCName = '대상이 UNC 이름 인 경우 PropertyStoreDataBlock을 저장하십시오.';
RS_TargetFileProp = '대상 파일 속성';
RS_TargetFileCreateDT = '대상 파일 생성일';
RS_TargetFileAccessDT = '대상 파일 사용일';
RS_TargetFileModifyDT = '대상 파일 수정일';
RS_TargetFileSize = '대상 파일 크기';
RS_IconIndex = '아이콘 인덱스';
RS_HotKey = '바로 가기 키';
RS_ReservedField1 = '예약 필드1';
RS_ReservedField2 = '예약 필드2';
RS_ReservedField3 = '예약 필드3';
RS_TargetFilePath = '대상 파일 위치';
RS_Network = '네트워크';
RS_Local = '로컬';
RS_TargetFilePathInfo = '대상 파일 경로 정보';
RS_VolumeName = '볼륨 이름';
RS_VolumeType = '볼륨 종류';
function GetLfiValueFromCaption(aList: TLfiEntList; sCaption: String): String;
var
i: Integer;
begin
Result := '';
if aList = nil then
exit;
for i := 0 to aList.Count - 1 do
begin
if CompareText(aList[i].sCaption, sCaption) = 0 then
begin
Result := aList[i].sValue;
exit;
end;
Result := GetLfiValueFromCaption(aList[i].ChildList, sCaption);
if Result <> '' then
exit;
end;
end;
{ TParserLinkFile }
Constructor TParserLinkFile.Create;
begin
Inherited Create;
LfiEntList_ := TLfiEntList.Create;
LfiEntList_.OnNotify := OnEntNotify;
end;
Destructor TParserLinkFile.Destroy;
begin
FreeAndNil(LfiEntList_);
Inherited;
end;
procedure TParserLinkFile.OnEntNotify(Sender: TObject; const Item: PLfiEnt; Action: TCollectionNotification);
begin
case Action of
cnAdded: ;
cnRemoved:
begin
if Item.ChildList <> nil then
FreeAndNil(Item.ChildList);
Dispose(Item);
end;
cnExtracted: ;
end;
end;
function TParserLinkFile.AddLinkFileInfo(pParent: PLfiEnt; sCaption, sValue: String; nInsertIdx: Integer = -1): PLfiEnt;
begin
New(Result);
ZeroMemory(Result, SizeOf(TLfiEnt));
Result.sCaption := sCaption;
Result.sValue := sValue;
if pParent <> nil then
begin
if pParent.ChildList = nil then
begin
pParent.ChildList := TLfiEntList.Create;
pParent.ChildList.OnNotify := OnEntNotify;
end;
if (nInsertIdx >= 0) and (nInsertIdx < pParent.ChildList.Count) then
pParent.ChildList.Insert(nInsertIdx, Result)
else
pParent.ChildList.Add(Result);
end else begin
if (nInsertIdx >= 0) and (nInsertIdx < LfiEntList_.Count) then
LfiEntList_.Insert(nInsertIdx, Result)
else
LfiEntList_.Add(Result);
end;
end;
function TParserLinkFile.LoadFromStream(aStream: TStream; bClear: Boolean = true; nIncUtcMin: Integer = -1): Boolean;
function GetAnsiString: AnsiString;
var
b: Byte;
begin
Result := '';
while aStream.Size > aStream.Position do
begin
if aStream.Read(b, 1) <> 1 then
exit;
if b = 0 then
exit;
Result := Result + AnsiChar(b);
end;
end;
function GetUnicodeString: String;
var
wLen, w: WORD;
begin
Result := '';
if aStream.Read(wLen, 2) <> 2 then
exit;
while aStream.Size > aStream.Position do
begin
if wLen = 0 then
exit;
if aStream.Read(w, 2) <> 2 then
exit;
if w = 0 then
exit;
Result := Result + Char(w);
Dec(wLen);
end;
end;
var
LFH: TLinkFileHeader;
pParent, pEnt, pSubEnt: PLfiEnt;
wListLen,
wTargetIdListSize: WORD;
i: Integer;
// dwPos: DWORD;
LFLI: TLinkFileLocationInfo;
LFLVT: TLinkFileLocalVolumeTable;
LFNVT: TLinkFileNetworkVolumeTable;
sTemp: String;
begin
Result := true;
try
if bClear then
LfiEntList_.Clear;
ZeroMemory(@LFH, SizeOf(LFH));
aStream.Read(LFH, SizeOf(LFH));
if AnsiString(@LFH)[1] = '[' then
exit;
if nIncUtcMin = -1 then
nIncUtcMin := (TTimeZone.Local.UtcOffset.Hours * 60) + TTimeZone.Local.UtcOffset.Minutes;
pParent := AddLinkFileInfo(nil, RS_LinkFIleHeaderInfo, '');
AddLinkFileInfo(pParent, RS_HeaderSize, Format('%s (%d)', [ByteSizeToStr(LFH.dwHeaderSize), LFH.dwHeaderSize]));
AddLinkFileInfo(pParent, 'CLSID', GUIDToString(LFH.CLSID));
pEnt := AddLinkFileInfo(pParent, 'Link Flags', IntToStr(LFH.dwLinkFlags));
if (LFH.dwLinkFlags and HasLinkTargetIDList) <> 0 then
AddLinkFileInfo(pEnt, 'HasLinkTargetIDList', RS_ExistsIDListInShellHeader)
else
AddLinkFileInfo(pEnt, 'HasLinkTargetIDList', RS_NoExistsIDListInShellHeader);
if (LFH.dwLinkFlags and HasLinkInfo) <> 0 then
AddLinkFileInfo(pEnt, 'HasLinkInfo', RS_LinkInfoPresent)
else
AddLinkFileInfo(pEnt, 'HasLinkInfo', RS_NoLinkInfoAvailable);
if (LFH.dwLinkFlags and HasName) <> 0 then
AddLinkFileInfo(pEnt, 'HasName', RS_ShellLinkNamePresent);
if (LFH.dwLinkFlags and HasRelativePath) <> 0 then
AddLinkFileInfo(pEnt, 'HasRelativePath', RS_RelativeShellLinkPathUsed);
if (LFH.dwLinkFlags and HasWorkingDir) <> 0 then
AddLinkFileInfo(pEnt, 'HasWorkingDir', RS_ShellWorkingDirEntered);
if (LFH.dwLinkFlags and HasArguments) <> 0 then
AddLinkFileInfo(pEnt, 'HasArguments', RS_CommandLineArgumentsEntered);
if (LFH.dwLinkFlags and HasIconLocation) <> 0 then
AddLinkFileInfo(pEnt, 'HasIconLocation', RS_IconLocationEntered);
if (LFH.dwLinkFlags and IsUnicode) <> 0 then
AddLinkFileInfo(pEnt, 'IsUnicode', RS_LinkContainsUnicode);
if (LFH.dwLinkFlags and ForceNoLinkInfo) <> 0 then
AddLinkFileInfo(pEnt, 'ForceNoLinkInfo', RS_LinkInfoStructureIgnored);
if (LFH.dwLinkFlags and HasExpString) <> 0 then
AddLinkFileInfo(pEnt, 'HasExpString', RS_LinkSavedInEnvironmentVariableDataBlock);
if (LFH.dwLinkFlags and RunInSeparateProcess) <> 0 then
AddLinkFileInfo(pEnt, 'RunInSeparateProcess', RS_RunTargetInVirtualMachine);
if (LFH.dwLinkFlags and Unused1) <> 0 then
AddLinkFileInfo(pEnt, 'Unused1', 'unused? Unknown! Bit 0x800');
if (LFH.dwLinkFlags and HasDarwinID) <> 0 then
AddLinkFileInfo(pEnt, 'HasDarwinID', RS_LinkHasDrawinSection);
if (LFH.dwLinkFlags and RunAsUser) <> 0 then
AddLinkFileInfo(pEnt, 'RunAsUser', RS_LinkRunsAsDifferentUser);
if (LFH.dwLinkFlags and HasExpIcon) <> 0 then
AddLinkFileInfo(pEnt, 'HasExpIcon', RS_LinkSavedWithIconEnvironmentDataBlock);
if (LFH.dwLinkFlags and NoPidlAlias) <> 0 then
AddLinkFileInfo(pEnt, 'NoPidlAlias', RS_FileSystemLocationRepresentedInShellNamespace);
if (LFH.dwLinkFlags and Unused2) <> 0 then
AddLinkFileInfo(pEnt, 'Unused2', 'unused? Unknown! Bit 0x10000');
if (LFH.dwLinkFlags and RunWithShimLayer) <> 0 then
AddLinkFileInfo(pEnt, 'RunWithShimLayer', RS_ShellLinkHasShimDataBlock);
if (LFH.dwLinkFlags and ForceNoLinkTrack) <> 0 then
AddLinkFileInfo(pEnt, 'ForceNoLinkTrack', RS_TrackerDataBlockIgnored);
if (LFH.dwLinkFlags and EnableTargetMetadata) <> 0 then
AddLinkFileInfo(pEnt, 'EnableTargetMetadata', RS_ShellLinkCollectsTargetProperties);
if (LFH.dwLinkFlags and DisableLinkPathTracking) <> 0 then
AddLinkFileInfo(pEnt, 'DisableLinkPathTracking', RS_EnvironmentVariableDataBlockIgnored);
if (LFH.dwLinkFlags and DisableKnownFolderTracking) <> 0 then
AddLinkFileInfo(pEnt, 'DisableKnownFolderTracking', RS_SpecialFolderDataBlockNKnownFolderDataBlockIgnored);
if (LFH.dwLinkFlags and DisableKnownFolderAlias) <> 0 then
AddLinkFileInfo(pEnt, 'DisableKnownFolderAlias', RS_KnownFolderDataBlock4TranslatingTargetIDList);
if (LFH.dwLinkFlags and AllowLinkToLink) <> 0 then
AddLinkFileInfo(pEnt, 'AllowLinkToLink', RS_CreatingLinkReferncesAnotherLink);
if (LFH.dwLinkFlags and UnaliasOnSave) <> 0 then
AddLinkFileInfo(pEnt, 'UnaliasOnSave', RS_UseUnaliased);
if (LFH.dwLinkFlags and PreferEnvironmentPath) <> 0 then
AddLinkFileInfo(pEnt, 'PreferEnvironmentPath', RS_TargetIDListShouldNotStored);
if (LFH.dwLinkFlags and KeepLocalIDListForUNCTarget) <> 0 then
AddLinkFileInfo(pEnt, 'KeepLocalIDListForUNCTarget', RS_StorePropertyStoreDataBlockIfTargetUNCName);
if (LFH.dwLinkFlags and Unused3) <> 0 then
AddLinkFileInfo(pEnt, 'Unused3', 'unused? Unknown! Bit 0x8000000');
if (LFH.dwLinkFlags and Unused4) <> 0 then
AddLinkFileInfo(pEnt, 'Unused4', 'unused? Unknown! Bit 0x10000000');
if (LFH.dwLinkFlags and Unused5) <> 0 then
AddLinkFileInfo(pEnt, 'Unused5', 'unused? Unknown! Bit 0x20000000');
if (LFH.dwLinkFlags and Unused6) <> 0 then
AddLinkFileInfo(pEnt, 'Unused6', 'unused? Unknown! Bit 0x40000000');
if (LFH.dwLinkFlags and Unused7) <> 0 then
AddLinkFileInfo(pEnt, 'Unused7', 'unused? Unknown! Bit 0x80000000');
if LFH.dwFileAttrFlags <> 0 then
AddLinkFileInfo(pParent, RS_TargetFileProp, Format('%s (%d)', [ConvFileAttrToStr(LFH.dwFileAttrFlags), LFH.dwFileAttrFlags]));
if (LFH.ftCreate.dwLowDateTime <> 0) and (LFH.ftCreate.dwHighDateTime <> 0) then
AddLinkFileInfo(pParent, RS_TargetFileCreateDT, DateTimeToStr(IncMinute(ConvFileTimeToDateTime(LFH.ftCreate), nIncUtcMin)));
if (LFH.ftAccess.dwLowDateTime <> 0) and (LFH.ftAccess.dwHighDateTime <> 0) then
AddLinkFileInfo(pParent, RS_TargetFileAccessDT, DateTimeToStr(IncMinute(ConvFileTimeToDateTime(LFH.ftAccess), nIncUtcMin)));
if (LFH.ftModify.dwLowDateTime <> 0) and (LFH.ftModify.dwHighDateTime <> 0) then
AddLinkFileInfo(pParent, RS_TargetFileModifyDT, DateTimeToStr(IncMinute(ConvFileTimeToDateTime(LFH.ftModify), nIncUtcMin)));
if LFH.dwFileSize <> 0 then
AddLinkFileInfo(pParent, RS_TargetFileSize, Format('%s (%d)', [ByteSizeToStr(LFH.dwFileSize), LFH.dwFileSize]));
AddLinkFileInfo(pParent, RS_IconIndex, IntToStr(LFH.dwIconIndex));
AddLinkFileInfo(pParent, RS_HotKey, ConvHotkeyToStr(LFH.wHotKey));
AddLinkFileInfo(pParent, RS_ReservedField1, IntToStr(LFH.wPatch));
AddLinkFileInfo(pParent, RS_ReservedField2, IntToStr(LFH.dwReserved1));
AddLinkFileInfo(pParent, RS_ReservedField3, IntToStr(LFH.dwReserved2));
if (LFH.dwLinkFlags and HasLinkTargetIDList) <> 0 then
begin
wTargetIdListSize := 0;
aStream.Read(wTargetIdListSize, 2);
pEnt := AddLinkFileInfo(pParent, 'Target ID List', Format('0x%.4X (Size=%d)', [aStream.Position - 2, wTargetIdListSize]));
if wTargetIdListSize > 0 then
begin
i := 1;
while aStream.Size > aStream.Position do
begin
if aStream.Read(wListLen, 2) <> 2 then
begin
AddLinkFileInfo(pEnt, 'EOF', '....');
exit;
end;
if wListLen <= 2 then
begin
AddLinkFileInfo(pEnt, 'Terminator found', '');
break;
end;
AddLinkFileInfo(pEnt, Format('ID List entry - %.2d', [i]),
Format('0x%.4X (Size=%d)', [aStream.Position - 2, wListLen]));
aStream.Position := aStream.Position + (wListLen - 2);
Inc(i);
end;
end;
end;
pEnt := nil;
if (LFH.dwLinkFlags and HasLinkInfo) <> 0 then
begin
pEnt := AddLinkFileInfo(pParent, 'Link Info List', Format('0x%.4X', [aStream.Position]));
// dwPos := aStream.Position;
ZeroMemory(@LFLI, SizeOf(LFLI));
if aStream.Read(LFLI, SizeOf(LFLI)) <> SizeOf(LFLI) then
begin
AddLinkFileInfo(pEnt, 'EOF', '....');
exit;
end;
AddLinkFileInfo(pEnt, 'Table Length',
Format('%s (%d)', [ByteSizeToStr(LFLI.dwLinkInfoSize), LFLI.dwLinkInfoSize]));
AddLinkFileInfo(pEnt, 'Header Length',
Format('%s (%d)', [ByteSizeToStr(LFLI.dwLinkInfoHeaderSize), LFLI.dwLinkInfoHeaderSize]));
if LFLI.dwLinkInfoFlags <> 0 then
begin
pSubEnt := AddLinkFileInfo(pEnt, 'Link Info Flags', '');
if (LFLI.dwLinkInfoFlags and VolumeIDAndLocalBasePath) <> 0 then
AddLinkFileInfo(pSubEnt, 'Flags 0x1', 'VolumeIDAndLocalBasePath');
if (LFLI.dwLinkInfoFlags and CommonNetworkRelativeLinkAndPathSuffix) <> 0 then
begin
AddLinkFileInfo(pSubEnt, 'Flags 0x2', 'CommonNetworkRelativeLinkAndPathSuffix');
AddLinkFileInfo(pSubEnt, RS_TargetFilePath, RS_Network);
end else
AddLinkFileInfo(pSubEnt, RS_TargetFilePath, RS_Local);
end;
pSubEnt := nil;
if (LFLI.dwLinkInfoFlags and VolumeIDAndLocalBasePath) <> 0 then
begin
pSubEnt := AddLinkFileInfo(nil, RS_TargetFilePathInfo, '', 0);
// 이 부분은 굳이 구현하지 않아도 될거 같아서 패스
if LFLI.dwLinkInfoHeaderSize > $1C then
begin
(*
buff = reader.ReadBytes(4);
LocalBasePathOffsetUnicode = ptrToInt32(buff);
basePath = (UInt32)fs.Position + LocalBasePathOffsetUnicode - 4;
dataGridView1.Rows.Add("Base Path", "@ 0x" + makeLen8String(basePath, 4));
buff = reader.ReadBytes(4);
CommonPathSuffixOffsetUnicode = ptrToInt32(buff);
if (LinkFileLocations.LinkInfoHeaderSize >= 0x24)
{
suffix = basePath + CommonPathSuffixOffsetUnicode;
dataGridView1.Rows.Add("Suffix", "@ 0x" + makeLen8String(suffix, 4));
}
*)
aStream.Position := aStream.Position + 4; // buff = reader.ReadBytes(4);
aStream.Position := aStream.Position + 4; // buff = reader.ReadBytes(4);
end;
ZeroMemory(@LFLVT, SizeOf(LFLVT));
if aStream.Read(LFLVT, SizeOf(LFLVT)) <> SizeOf(LFLVT) then
begin
AddLinkFileInfo(pSubEnt, 'EOF', '....');
exit;
end;
AddLinkFileInfo(pSubEnt, RS_VolumeName, GetAnsiString);
case LFLVT.dwVolumeType of
1 : sTemp := 'No root directory';
2 : sTemp := 'Removable (Floppy, Zip, etc..)';
3 : sTemp := 'Fixed (Hard disk)';
4 : sTemp := 'Remote (Network drive)';
5 : sTemp := 'CD-ROM';
6 : sTemp := 'Ram drive';
else sTemp := 'Unknown';
end;
AddLinkFileInfo(pSubEnt, RS_VolumeType, sTemp);
// ........
(*
dataGridView2.Rows.Add(" Volume Entries:");
dataGridView2.Rows.Add(" Volume Length 0x" + makeLen8String((UInt32)LocalVolumeTable.volumeTableLength, 4) + " Bytes");
dataGridView2.Rows.Add(" Volume Type = 0x" + makeLen8String((UInt32)LocalVolumeTable.volumeType, 4) + " -" + shlp);
dataGridView2.Rows.Add(" Volume Serial # 0x" + makeLen8String((UInt32)LocalVolumeTable.volumeSerialNbr, 0) +
" (" + (long)LocalVolumeTable.volumeSerialNbr + ")");
dataGridView2.Rows.Add(" Volume Name @ 0x" + makeLen8String((UInt32)LocalVolumeTable.offsetVolumeName + loc, 4));
*)
end;
if ((LFLI.dwLinkInfoFlags and VolumeIDAndLocalBasePath) = 0) and
((LFLI.dwLinkInfoFlags and CommonNetworkRelativeLinkAndPathSuffix) <> 0) then
begin
if pSubEnt = nil then
pSubEnt := AddLinkFileInfo(nil, RS_TargetFilePathInfo, '', 0); //가장 위에 보이도록 수정
ZeroMemory(@LFNVT, SizeOf(LFNVT));
if aStream.Read(LFNVT, SizeOf(LFNVT)) <> SizeOf(LFNVT) then
begin
AddLinkFileInfo(pSubEnt, 'EOF', '....');
exit;
end;
if LFNVT.dwNetNameOffset > $14 then
begin
{
buff = reader.ReadBytes(4);
NetNameOffsetUnicode = ptrToInt32(buff);
buff = reader.ReadBytes(4);
DeviceNameOffsetUnicode = ptrToInt32(buff);
}
aStream.Position := aStream.Position + 4; // buff = reader.ReadBytes(4);
aStream.Position := aStream.Position + 4; // buff = reader.ReadBytes(4);
end;
if (LFNVT.dwNetFlags and ValidNetType) <> 0 then
begin
case LFNVT.dwNetProviderType of
$00020000 : sTemp := 'WNNC_NET_SMB';
$00440000 : sTemp := 'WNNC_NET_NDFS';
$001A0000 : sTemp := 'WNNC_NET_AVID';
$001B0000 : sTemp := 'WNNC_NET_DOCUSPACE';
$001C0000 : sTemp := 'WNNC_NET_MANGOSOFT';
$001D0000 : sTemp := 'WNNC_NET_SERNET';
$001E0000 : sTemp := 'WNNC_NET_RIVERFRONT1';
$001F0000 : sTemp := 'WNNC_NET_RIVERFRONT2';
$00200000 : sTemp := 'WNNC_NET_DECORB';
$00210000 : sTemp := 'WNNC_NET_PROTSTOR';
$00220000 : sTemp := 'WNNC_NET_FJ_REDIR';
$00230000 : sTemp := 'WNNC_NET_DISTINCT';
$00240000 : sTemp := 'WNNC_NET_TWINS';
$00250000 : sTemp := 'WNNC_NET_RDR2SAMPLE';
$00260000 : sTemp := 'WNNC_NET_CSC';
$00270000 : sTemp := 'WNNC_NET_3IN1';
$00290000 : sTemp := 'WNNC_NET_EXTENDNET';
$002A0000 : sTemp := 'WNNC_NET_STAC';
$002B0000 : sTemp := 'WNNC_NET_FOXBAT';
$002C0000 : sTemp := 'WNNC_NET_YAHOO';
$002D0000 : sTemp := 'WNNC_NET_EXIFS';
$002E0000 : sTemp := 'WNNC_NET_DAV';
$002F0000 : sTemp := 'WNNC_NET_KNOWARE';
$00300000 : sTemp := 'WNNC_NET_OBJECT_DIRE';
$00310000 : sTemp := 'WNNC_NET_MASFAX';
$00320000 : sTemp := 'WNNC_NET_HOB_NFS';
$00330000 : sTemp := 'WNNC_NET_SHIVA';
$00340000 : sTemp := 'WNNC_NET_IBMAL';
$00350000 : sTemp := 'WNNC_NET_LOCK';
$00360000 : sTemp := 'WNNC_NET_TERMSRV';
$00370000 : sTemp := 'WNNC_NET_SRT';
$00380000 : sTemp := 'WNNC_NET_QUINCY';
$00390000 : sTemp := 'WNNC_NET_OPENAFS';
$003A0000 : sTemp := 'WNNC_NET_AVID1';
$003B0000 : sTemp := 'WNNC_NET_DFS';
$003C0000 : sTemp := 'WNNC_NET_KWNP';
$003D0000 : sTemp := 'WNNC_NET_ZENWORKS';
$003E0000 : sTemp := 'WNNC_NET_DRIVEONWEB';
$003F0000 : sTemp := 'WNNC_NET_VMWARE';
$00400000 : sTemp := 'WNNC_NET_RSFX';
$00410000 : sTemp := 'WNNC_NET_MFILES';
$00420000 : sTemp := 'WNNC_NET_MS_NFS';
$00430000 : sTemp := 'WNNC_NET_GOOGLE';
else sTemp := 'Unknown - ' + Format('%.8X (%d)', [LFNVT.dwNetProviderType, LFNVT.dwNetProviderType]);
end;
AddLinkFileInfo(pSubEnt, 'Network Provider Type', sTemp);
end;
AddLinkFileInfo(pSubEnt, 'Network Relative Link', GetAnsiString);
sTemp := GetAnsiString;
if sTemp <> '' then
begin
AddLinkFileInfo(pSubEnt, 'File Name', ExtractFileName(sTemp));
AddLinkFileInfo(pSubEnt, 'Net Name', sTemp);
end;
exit;
end;
sTemp := GetAnsiString;
if sTemp <> '' then
begin
if CompareText('c:\users\', sTemp) = 0 then
begin
// 윈도우 11 최근 업데이트 부터... 바탕화면에 있는 파일이 최근 문서(LNK)로 등록될때
// 기존처럼 가져와 지지 않는다... 아래처럼 추가 처리 필요... 23_0823 18:47:19 kku
aStream.Position := aStream.Position + 21;
GetAnsiString; // \\KKU-PC\Users << 이런 형태
sTemp := sTemp + GetAnsiString;
end;
AddLinkFileInfo(pSubEnt, 'File Name', ExtractFileName(sTemp));
AddLinkFileInfo(pSubEnt, 'Base Path', sTemp);
end;
sTemp := GetAnsiString;
if sTemp <> '' then
AddLinkFileInfo(pSubEnt, 'Common Base Path', GetUnicodeString);
if (LFH.dwLinkFlags and HasName) <> 0 then
AddLinkFileInfo(pSubEnt, 'Name', GetUnicodeString);
if (LFH.dwLinkFlags and HasRelativePath) <> 0 then
AddLinkFileInfo(pSubEnt, 'Relative Path', GetUnicodeString);
if (LFH.dwLinkFlags and HasWorkingDir) <> 0 then
AddLinkFileInfo(pSubEnt, 'Working Directory', GetUnicodeString);
if (LFH.dwLinkFlags and HasArguments) <> 0 then
AddLinkFileInfo(pSubEnt, 'Arguments', GetUnicodeString);
if (LFH.dwLinkFlags and HasIconLocation) <> 0 then
AddLinkFileInfo(pSubEnt, 'Icon Location', GetUnicodeString);
end;
except
Result := false;
end;
end;
function TParserLinkFile.LoadFromFile(sPath: String): Boolean;
var
fs: TFileStream;
pParent: PLfiEnt;
dtCreate, dtAccess, dtModify: TDateTime;
begin
Result := false;
try
LfiEntList_.Clear;
if not FileExists(sPath) then
exit;
pParent := AddLinkFileInfo(nil, RS_LinkFIleInfo, '');
AddLinkFileInfo(pParent, RS_Name, ExtractFileName(sPath));
AddLinkFileInfo(pParent, RS_Path, ExtractFilePath(sPath));
if GetFileDateTime_Local(sPath, dtCreate, dtModify, dtAccess) then
begin
AddLinkFileInfo(pParent, RS_CreateDT, DateTimeToStr(dtCreate));
AddLinkFileInfo(pParent, RS_AccessDT, DateTimeToStr(dtAccess));
AddLinkFileInfo(pParent, RS_ModifyDT, DateTimeToStr(dtModify));
end;
Guard(fs, TFileStream.Create(sPath, fmOpenReadWrite or fmShareDenyNone));
Result := LoadFromStream(fs, false);
except
// ..
end;
end;
end.