#include-Once
;ReadLnkFile.au3
; версия 1.0 от 17.09.2014
;*****************************************************************************
; Author AU3.............: glax24
;*****************************************************************************
;_FileGetPathTargetLnk_Example()
;_FileGetPathTargetLnk($sPathFullLnk)
;_FileGetPathTargetLnk_($sPathFullLnk);
Func _FileGetPathTargetLnk_Example()
Local $sPathLnk = 'c:\Users\Public\Desktop\Git Bash.lnk'
Local $sPathTarget = _FileGetPathTargetLnk($sPathLnk)
MsgBox(0, 'PathTarget', $sPathTarget & @CRLF & 'error = ' & @error)
EndFunc ;==>_ReadLnkFile_Example
; #FUNCTION# ;=================================================================================
; Function Name ...: _ReadLnkFile
; AutoIt Version ....: 3.3.8.1+
; Description ........: Get target file (full local path) from a link file (.lnk)
; Syntax................: _ReadLnkFile($sPathFullLnk)
; Parameters........: $sPathFullLnk - Full path and file name of the shortcut.
; Return values:
; |Success - Shortcut target path
; |Failure - @error>0
; Author(s) ..........: glax24
; ============================================================================================
; Имя функции ...: _ReadLnkFile
; Версия AutoIt ..: 3.3.8.1+
; Описание ........: Возвращает полный путь объекта запуска ярлыка (.lnk)
; Синтаксис.......: _ReadLnkFile($sPathFullLnk)
; Параметры.....: $sPathFullLnk - Полный путь и имя файла ярлыка.
; Возвращаемое значение:
; |Успешно - Путь к объекту запуска
; |Неудачно - @error>0
; Автор ..........: glax24
; ============================================================================================
Func _FileGetPathTargetLnk($sPathFullLnk)
;-------------------------------------------------------------------------
; Get the .lnk-file content:
;-------------------------------------------------------------------------
Local $hFile = FileOpen($sPathFullLnk, 16); rb
If $hFile = -1 Then
Return SetError(1, 0, '')
EndIf
Local $iSizeFile, $i
Local $bByte
Local $bDataFile = FileRead($hFile)
If @error = 1 Then
Return SetError(2, 0, '')
EndIf
FileClose($hFile)
$iSizeFile = BinaryLen($bDataFile)
;-------------------------------------------------------------------------
; Check the magic value (first byte) and the GUID (16 byte from 5th byte):
;-------------------------------------------------------------------------
; The GUID is telling the version of the .lnk-file format. We expect the
; following GUID (HEX): 01 14 02 00 00 00 00 00 C0 00 00 00 00 00 00 46.
;-------------------------------------------------------------------------
If ($iSizeFile < 20) Then
Return SetError(3, 0, '')
EndIf
$bByte = BinaryMid($bDataFile, 1, 1)
;test the magic value 'L'
If $bByte <> 0x4C Then
Return SetError(4, 0, '')
EndIf
;test the GUID
$bByte = BinaryMid($bDataFile, 5, 16)
If $bByte <> Binary('0x0114020000000000C000000000000046') Then
Return SetError(5, 0, '')
EndIf
;-------------------------------------------------------------------------
; Get the flags (4 byte from 21st byte):
;-------------------------------------------------------------------------
; Check if it points to a file or directory!
;-------------------------------------------------------------------------
; Flags (4 byte little endian):
; Bit 0 -> has shell item id list
; Bit 1 -> points to file or directory
; Bit 2 -> has description
; Bit 3 -> has relative path
; Bit 4 -> has working directory
; Bit 5 -> has commandline arguments
; Bit 6 -> has custom icon
;-------------------------------------------------------------------------
$i = 20
If $iSizeFile < ($i + 4) Then
Return SetError(6, 0, '')
EndIf
$bByte = BinaryMid($bDataFile, $i + 1, 4)
Local $idwFlags = Int($bByte); //little endian format
;ConsoleWrite("$idwFlags = " & $idwFlags & @CRLF)
Local $bHasShellItemIdList = BitAND($idwFlags, 0x00000001)
Local $bPointsToFileOrDir = BitAND($idwFlags, 0x00000002)
;ConsoleWrite("$bHasShellItemIdList = " & $bHasShellItemIdList & @CRLF)
;ConsoleWrite("$bPointsToFileOrDir = " & $bPointsToFileOrDir & @CRLF)
If Not $bPointsToFileOrDir Then
Return SetError(7, 0, '')
EndIf
;-------------------------------------------------------------------------
; Shell item id list (starts at 76 with 2 byte length -> so we can skip):
;-------------------------------------------------------------------------
Local $iA = -6
If $bHasShellItemIdList Then
$i = 76
If $iSizeFile < ($i + 2) Then
Return SetError(8, 0, '')
EndIf
$bByte = BinaryMid($bDataFile, $i + 1, 2)
$iA = Int($bByte); //little endian format
EndIf
;ConsoleWrite("$iA = " & $iA & @CRLF)
;-------------------------------------------------------------------------
; File location info:
;-------------------------------------------------------------------------
; Follows the shell item id list and starts with 4 byte structure length,
; followed by 4 byte offset for skipping.
;-------------------------------------------------------------------------
$i = 78 + 4 + $iA
If $iSizeFile < ($i + 4) Then
Return SetError(9, 0, '')
EndIf
$bByte = BinaryMid($bDataFile, $i + 1, 4)
Local $iB = Int($bByte); //little endian format
;ConsoleWrite("$iB = " & $iB & @CRLF)
;-------------------------------------------------------------------------
; Local volume table:
;-------------------------------------------------------------------------
; Follows the file location info and starts with 4 byte table length for
; skipping the actual table and moving to the local path string.
;-------------------------------------------------------------------------
$i = 78 + $iA + $iB
If $iSizeFile < ($i + 4) Then
Return SetError(10, 0, '')
EndIf
$bByte = BinaryMid($bDataFile, $i + 1, 4)
Local $iC = Int($bByte) ; //little endian format
;ConsoleWrite("$iC = " & $iC & @CRLF)
;-------------------------------------------------------------------------
; Local path string (ending with 0x00):
;-------------------------------------------------------------------------
$i = 78 + $iA + $iB + $iC
If $iSizeFile < ($i + 1) Then
Return SetError(11, 0, '')
EndIf
Local $strLinkedTarget = ''
While $i < $iSizeFile
$bByte = BinaryMid($bDataFile, $i + 1, 1)
If $bByte = 0x00 Then
ExitLoop
EndIf
$strLinkedTarget &= Chr($bByte)
$i += 1
WEnd
;Return if empty:
If Not $strLinkedTarget Then
Return SetError(12, 0, '')
EndIf
If StringInStr($strLinkedTarget, '~') Then
Local $strLinkedTargetLong = FileGetLongName($strLinkedTarget)
If Not @error Then
$strLinkedTarget = $strLinkedTargetLong
EndIf
EndIf
;ConsoleWrite('strLinkedTarget = [' & $strLinkedTarget & ']' & @CRLF)
Return $strLinkedTarget
EndFunc ;==>_ReadLnkFile
Func _FileGetPathTargetLnk_($sPathFullLnk)
Local $iSizeFile, $iCountByteRead = 0, $bDataFile
Local $iflags, $itemIdLength, $istart, $ilength, $ioffset, $ilength_path
Local $sPathFull, $aPathFull, $sPathChar, $bByte
Local $sPathFullLong
Local $hFile = FileOpen($sPathFullLnk, 16)
; Проверяет, является ли файл открытым, перед тем как использовать функции чтения/записи в файл
If $hFile = -1 Then
Return SetError(1, 0, '')
EndIf
;--------------------------------------------
$bDataFile = FileRead($hFile)
If @error = 1 Then
Return SetError(1, 0, '')
EndIf
$iSizeFile = BinaryLen($bDataFile)
;ConsoleWrite('$iSizeFile ' & $iSizeFile & @CRLF)
FileSetPos($hFile, 0, 0) ;begin
;--------------------------------------------
If Not FileSetPos($hFile, 0x14, 0) Then ;begin
FileClose($hFile)
Return SetError(2, 0, '')
EndIf
$iCountByteRead += 4
If $iSizeFile < $iCountByteRead Then
Return SetError(6, 0, '')
EndIf
$iflags = FileRead($hFile, 4)
If BitAND($iflags, 0x01) = 1 Then
If Not FileSetPos($hFile, 0x4c, 0) Then ;begin
FileClose($hFile)
Return SetError(2, 0, '')
EndIf
$iCountByteRead += 2
If $iSizeFile < $iCountByteRead Then
Return SetError(6, 0, '')
EndIf
$itemIdLength = FileRead($hFile, 2)
$itemIdLength = Int($itemIdLength)
If Not FileSetPos($hFile, $itemIdLength, 1) Then ;current
FileClose($hFile)
Return SetError(2, 0, '')
EndIf
EndIf
$istart = FileGetPos($hFile)
$iCountByteRead += 4
If $iSizeFile < $iCountByteRead Then
Return SetError(6, 0, '')
EndIf
$ilength = FileRead($hFile, 4)
$ilength = Int($ilength)
If Not FileSetPos($hFile, 0x0c, 1) Then ;current
FileClose($hFile)
Return SetError(2, 0, '')
EndIf
$iCountByteRead += 4
If $iSizeFile < $iCountByteRead Then
Return SetError(6, 0, '')
EndIf
$ioffset = FileRead($hFile, 4)
$ioffset = Int($ioffset)
If Not FileSetPos($hFile, $istart + $ioffset, 0) Then ;begin
FileClose($hFile)
Return SetError(2, 0, '')
EndIf
$ilength_path = Int(($istart + $ilength) - FileGetPos($hFile))
If $ilength_path < 0 Or Not $ilength_path Or ($iCountByteRead + $ilength_path) > $iSizeFile Then
Return SetError(3, 0, '')
EndIf
$sPathFull = FileRead($hFile, $ilength_path)
FileClose($hFile)
For $i = 1 To $ilength_path
$bByte = BinaryMid($sPathFull, $i, 1)
If $bByte = 0x00 Then
ExitLoop
EndIf
$sPathChar &= Chr($bByte)
Next
$sPathFull = $sPathChar
;ConsoleWrite('$sPathFull [' & $sPathFull & ']' & @CRLF)
If StringInStr($sPathFull, '~') Then
$sPathFullLong = FileGetLongName($sPathFull)
If Not @error Then
$sPathFull = $sPathFullLong
EndIf
EndIf
If $sPathFull Then
Return $sPathFull
Else
Return SetError(5, 0, '')
EndIf
EndFunc ;==>_GetPathTargetLnk
; Original func _FileGetPathTargetLnk
#cs
//*****************************************************************************
// File ..................: ReadLnkFile.cpp
// Description ...........: Decoder for .lnk files on WINDOWS
// Author ................: Peter Thoemmes (http://www.notes-about-cpp.com/)
//-----------------------------------------------------------------------------
// # !!! Disclaimer !!! #
// # This source code is provided "AS-IS" basis, without any warranties or #
// # representations express, implied or statutory; including, without #
// # limitation, warranties of quality, performance, non-infringement, #
// # merchantability or fitness for a particular purpose. Peter Thoemmes #
// # does not warrant that this source code will meet your needs or be free #
// # from errors. #
//-----------------------------------------------------------------------------
// Many thanks to Jesse Hager ([email protected]) for reverse engineering
// the shortcut file format and publishing the document 'The Windows Shortcut
// File Format'.
//-----------------------------------------------------------------------------
// Copyright (c) 2008 Peter Thoemmes, Weinbergstr. 3a, D-54441 Ockfen/Germany
//*****************************************************************************
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501
#define _WIN32_IE 0x0600
#include <string>
#include <vector>
#include <iostream>
#include <Windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
using namespace std;
string ReadLnkFile(const string&);
void using_();
int main(int argc, char *argv[]) {
if (argc == 1) {
using_();
return EXIT_FAILURE;
}
string Lnk = (const char*) argv[1];
string strFullTargetPath = ReadLnkFile(Lnk);
cout << strFullTargetPath << endl;
return EXIT_SUCCESS;
}
void using_() {
char pBuffer[256];
GetModuleFileName(GetModuleHandle(NULL), pBuffer, sizeof(pBuffer) );
PathStripPath(pBuffer);
cout << "ReadLnkFile by Peter Thoemmes" << endl << endl;
cout << "Using: " << pBuffer << " [Shortcut file name (.LNK)]" << endl << endl;
}
//-----------------------------------------------------------------------------
// Get target file (full local path) from a link file (.lnk):
//-----------------------------------------------------------------------------
string ReadLnkFile(const string& strFullLinkFileName)
{
//-------------------------------------------------------------------------
// How to read the target's path from a .lnk-file:
//-------------------------------------------------------------------------
// Problem:
//
// The COM interface to shell32.dll IShellLink::GetPath() fails!
//
// Solution:
//
// We need to parse the file manually. The path can be found like shown
// here, if the shell item id list is present. In case it is not present
// we have to assume A = -6, but not to parse/decode byte 76 and 77.
//
// +---------------------+-----------------------------------------------+
// | Index | Description |
// +---------------------+-----------------------------------------------+
// | 0 | 'L' (magic value) |
// +---------------------+-----------------------------------------------+
// | ... | ... |
// +---------------------+-----------------------------------------------+
// | 76 | A_0 |
// +---------------------+-----------------------------------------------+
// | 77 | A_1 (16 bit) [w/o shell item id list: A = -6] |
// +---------------------+-----------------------------------------------+
// | ... | ... |
// +---------------------+-----------------------------------------------+
// | 78 + 16 + A | B_0 |
// +---------------------+-----------------------------------------------+
// | 78 + 16 + A + 1 | B_1 |
// +---------------------+-----------------------------------------------+
// | 78 + 16 + A + 2 | B_2 |
// +---------------------+-----------------------------------------------+
// | 78 + 16 + A + 3 | B_3 (32 bit) |
// +---------------------+-----------------------------------------------+
// | ... | ... |
// +---------------------+-----------------------------------------------+
// | 78 + A + B | PATH_STR_0 |
// +---------------------+-----------------------------------------------+
// | 78 + A + B + 1 | PATH_STR_1 |
// +---------------------+-----------------------------------------------+
// | 78 + A + B + 2 | PATH_STR_2 |
// +---------------------+-----------------------------------------------+
// | ... | ... |
// +---------------------+-----------------------------------------------+
// | ... | 0x00 |
// +---------------------+-----------------------------------------------+
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// Get the .lnk-file content:
//-------------------------------------------------------------------------
FILE* pFile = fopen(strFullLinkFileName.c_str(),"rb");
if(!pFile)
return string("");
vector<unsigned char> vectBuffer;
unsigned char byte = '?';
while((byte = (unsigned char) fgetc(pFile)) != (unsigned char) EOF)
vectBuffer.push_back(byte);
fclose(pFile);
//-------------------------------------------------------------------------
// Check the magic value (first byte) and the GUID (16 byte from 5th byte):
//-------------------------------------------------------------------------
// The GUID is telling the version of the .lnk-file format. We expect the
// following GUID (HEX): 01 14 02 00 00 00 00 00 C0 00 00 00 00 00 00 46.
//-------------------------------------------------------------------------
if(vectBuffer.size() < 20)
return string("");
if(vectBuffer[0] != (unsigned char) 'L') //test the magic value
return string("");
if((vectBuffer[4] != 0x01) || //test the GUID
(vectBuffer[5] != 0x14) ||
(vectBuffer[6] != 0x02) ||
(vectBuffer[7] != 0x00) ||
(vectBuffer[8] != 0x00) ||
(vectBuffer[9] != 0x00) ||
(vectBuffer[10] != 0x00) ||
(vectBuffer[11] != 0x00) ||
(vectBuffer[12] != 0xC0) ||
(vectBuffer[13] != 0x00) ||
(vectBuffer[14] != 0x00) ||
(vectBuffer[15] != 0x00) ||
(vectBuffer[16] != 0x00) ||
(vectBuffer[17] != 0x00) ||
(vectBuffer[18] != 0x00) ||
(vectBuffer[19] != 0x46))
{
return string("");
}
//-------------------------------------------------------------------------
// Get the flags (4 byte from 21st byte):
//-------------------------------------------------------------------------
// Check if it points to a file or directory!
//-------------------------------------------------------------------------
// Flags (4 byte little endian):
// Bit 0 -> has shell item id list
// Bit 1 -> points to file or directory
// Bit 2 -> has description
// Bit 3 -> has relative path
// Bit 4 -> has working directory
// Bit 5 -> has commandline arguments
// Bit 6 -> has custom icon
//-------------------------------------------------------------------------
unsigned int i = 20;
if(vectBuffer.size() < (i + 4))
return string("");
unsigned int dwFlags = (unsigned int) vectBuffer[i]; //little endian format
dwFlags |= (((unsigned int) vectBuffer[++i]) << 8);
dwFlags |= (((unsigned int) vectBuffer[++i]) << 16);
dwFlags |= (((unsigned int) vectBuffer[++i]) << 24);
bool bHasShellItemIdList = (dwFlags & 0x00000001) ? true : false;
bool bPointsToFileOrDir = (dwFlags & 0x00000002) ? true : false;
if(!bPointsToFileOrDir)
return string("");
//-------------------------------------------------------------------------
// Shell item id list (starts at 76 with 2 byte length -> so we can skip):
//-------------------------------------------------------------------------
int A = -6;
if(bHasShellItemIdList)
{
i = 76;
if(vectBuffer.size() < (i + 2))
return string("");
A = (unsigned char) vectBuffer[i]; //little endian format
A |= (((unsigned char) vectBuffer[++i]) << 8);
}
//-------------------------------------------------------------------------
// File location info:
//-------------------------------------------------------------------------
// Follows the shell item id list and starts with 4 byte structure length,
// followed by 4 byte offset for skipping.
//-------------------------------------------------------------------------
i = 78 + 4 + A;
if(vectBuffer.size() < (i + 4))
return string("");
unsigned int B = (unsigned int) vectBuffer[i]; //little endian format
B |= (((unsigned int) vectBuffer[++i]) << 8);
B |= (((unsigned int) vectBuffer[++i]) << 16);
B |= (((unsigned int) vectBuffer[++i]) << 24);
//-------------------------------------------------------------------------
// Local volume table:
//-------------------------------------------------------------------------
// Follows the file location info and starts with 4 byte table length for
// skipping the actual table and moving to the local path string.
//-------------------------------------------------------------------------
i = 78 + A + B;
if(vectBuffer.size() < (i + 4))
return string("");
unsigned int C = (unsigned int) vectBuffer[i]; //little endian format
C |= (((unsigned int) vectBuffer[++i]) << 8);
C |= (((unsigned int) vectBuffer[++i]) << 16);
C |= (((unsigned int) vectBuffer[++i]) << 24);
//-------------------------------------------------------------------------
// Local path string (ending with 0x00):
//-------------------------------------------------------------------------
i = 78 + A + B + C;
if(vectBuffer.size() < (i + 1))
return string("");
string strLinkedTarget = "";
for(;i < vectBuffer.size();++i)
{
strLinkedTarget.append(1,(char) vectBuffer[i]);
if(!vectBuffer[i])
break;
}
//Return if empty:
if(strLinkedTarget.empty())
return string("");
//-------------------------------------------------------------------------
// Convert the target path into the long format (format without ~):
//-------------------------------------------------------------------------
// GetLongPathNameA() fails it the target file doesn't exist!
//-------------------------------------------------------------------------
char* szLinkedTargetLongFormat = new char[MAX_PATH + 1];
if(!szLinkedTargetLongFormat)
return string("");
if(!::GetLongPathNameA(
strLinkedTarget.c_str(),
szLinkedTargetLongFormat,
MAX_PATH))
{
return strLinkedTarget; //file doesn't exist
}
string strLinkedTargetLongFormat(szLinkedTargetLongFormat);
delete[] szLinkedTargetLongFormat;
return strLinkedTargetLongFormat;
}
#ce
; Original func _FileGetPathTargetLnk_
#cs
string GetFullPath(FileInfo file)
{
if (file.Extension == ".lnk") {
using (var fstream = File.Open(file.FullName, FileMode.Open, FileAccess.Read)) {
using (var reader = new BinaryReader(fstream)) {
fstream.Seek(0x14, SeekOrigin.Begin);
var flags = reader.ReadUInt32();
if (( flags & 0x01 ) == 1) {
fstream.Seek(0x4c, SeekOrigin.Begin);
var itemIdLength = reader.ReadUInt16();
fstream.Seek(itemIdLength, SeekOrigin.Current);
}
var start = fstream.Position;
var length = reader.ReadUInt32();
fstream.Seek(0x0c, SeekOrigin.Current);
var offset = reader.ReadUInt32();
fstream.Seek(start + offset, SeekOrigin.Begin);
return new string(reader.ReadChars((int)( start + length - fstream.Position )));
}
}
} else
return file.FullName;
}
#ce