Author Topic: Go To Link Target  (Read 17051 times)

pncdaspropagandas

  • Contributor
  • Active Member
  • *****
  • Posts: 93
    • View Profile
Go To Link Target
« on: February 10, 2017, 19:22:28 »
I did a script that when focusing on a Shortcut / Symbolic Link / Hardlink / Junction it navigates to it's target.

It's a mix of MC Script, Python and an executable (because it seems no one care about enumerating hardlinks targets)

If there's more than 2 instances of a hardlink or junction, the script asks the user witch one he likes.

I'm posting the main Python code (I used some custom libs I did). If people get interested I can post the full code.

MC Script:
@var $source_focus_path = '"' + GetSourceFocusPath() + '"';
@var $python_script_path;
@var $link_target;
@var $link_target_temp_file;
@var $link_target_file_size;
@var $findlinks_path;
@var $aux[];
@var $aux2;

$python_script_path = '"' + GetTagValue("${mcinstallpath}") ^ 'Tools\Links\Resolve Link.py' + '"';
$link_target_temp_file = GetTagValue("${mcinstallpath}") ^ 'Tools\Links\link_target.txt';
$findlinks_path = '"' + GetTagValue("${mcinstallpath}") ^ 'Tools\Links\Hardlink\FindLinks.exe' + '"';


if (FileExists($link_target_temp_file) == 1)
{
   $aux = {"NOPROGRESS", "NODIALOG", "SILENT"};
   DeleteFile($link_target_temp_file, $aux);
}

// There's a bug in StrReplace when using ^ in parameter
$aux2 = '^"';
$python_script_path    = StrReplace($python_script_path, '"', $aux2);
$source_focus_path     = StrReplace($source_focus_path, '"', $aux2);
$findlinks_path        = StrReplace($findlinks_path, '"', $aux2);
$link_target_temp_file = StrReplace($link_target_temp_file, '"', $aux2);
// Run Python script to get link target and store it in a temp file
LogAppInfo('"H:\Programas\MultiCommander - Projetos\Tools\Command Line Utilities\nircmd\nircmd_x64.exe" execmd "^"C:\Python35\python.exe^" ' + $python_script_path + ' ' + $source_focus_path + ' ^"' + $link_target_temp_file + '^" ' + $findlinks_path + '"');
MC.Run CMD='"H:\Programas\MultiCommander - Projetos\Tools\Command Line Utilities\nircmd\nircmd_x64.exe"' ARG={'execmd "^"C:\Python35\python.exe^" ' + $python_script_path + ' ' + $source_focus_path + ' ^"' + $link_target_temp_file + '^" ' + $findlinks_path + '"'} WAIT

// Clear selected files in the case of the link target being on the same folder
arrayAdd($aux, '');
SetSourceSelected($aux, 1);

// Ao rodar o nircmd ele retorna o sinal de terminado para o MultiComander antes de o Python ter terminado de rodar
// Então temos que esperar manualmente até o Python ter terminado de rodar
// A cada 50ms checamos se o arquivo $link_target_temp_file foi criado, até um timeout de 1s
@var $n;
for ($n = 0; $n < 20; $n++)
{
   if (FileExists($link_target_temp_file) == 1)
   {
      break;
   }
   Sleep(50);
}

// Go to target
if (FileExists($link_target_temp_file) == 1)
{
   Sleep(100);
   
   //LogAppInfo($link_target_temp_file);
   $link_target_file_size = GetFileSize($link_target_temp_file);
   if ($link_target_file_size > 0)
   {
      $link_target = LoadStringFromFile($link_target_temp_file);
      //LogAppInfo($link_target);
      
      // Delete temp file
      $aux = {"NOPROGRESS", "NODIALOG", "SILENT"};
      //DeleteFile($link_target_temp_file, $aux);
      
      // Clear selected files in the case of the link target being on the same folder
      arrayAdd($aux, '');
      SetSourceSelected($aux, 1);
      
      // As Mathias said here http://forum.multicommander.com/forum/index.php/topic,1929.0.html
      // "the UI can't be updated until the script is finished since the script is running in the same thread"
      // So there's no way to break going to the folder, then selecting the file in 2 operations
      // We can do it in 1 go using MC.RunCmd ID="Core.1312" that is CTRL+V with the target path on the clipboard
      // The ony downside is that we loose the clipboard content
      SetClipboardText($link_target);
      
      // Check if there's more than 1 link target (hardlinks and junctions)
      @var $link_targets[] = StrLines2Array($link_target);
      // If more than 1, ask the user
      if (arrayCount($link_targets) > 1)
      {
         @var $link_targets_answer = AskOption('Select target', $link_targets, 0);
         if ($link_targets_answer == -1)
         {
            // User cancelled
            break;
         }
         SetClipboardText($link_targets[$link_targets_answer]);
      }
      
      // Go To Link Target
      MC.RunCmd ID="Core.1312"
   }
   else
   {
      LogAppInfo('Error');
   }

}
else
{
   MessageBox('Resolve Link', 'Operation timeout', 0);
}


Python:

# -*- coding: utf-8 -*-

# argv[1] = link file/folder path
# argv[2] = path to exchange text file with Multi Commander
# argv[3] = path to FindLinks.exe

# argv[1] can't end with /
# Doesn't work when running inside Multi Commander and argv[1] is a symbolic link inside a junction (but it works inside cmd for example)

import sys
import os
import win32com.client
import mylib.windows.windows as lib_windows
import mylib.files.file_handling as lib_file_handling

file = open(sys.argv[2], 'w')

if sys.argv[1][-3:].lower() == 'lnk':
    print('Shortcut detected')
    shell = win32com.client.Dispatch("WScript.Shell")
    shortcut = shell.CreateShortCut(sys.argv[1])
    print(shortcut.Targetpath)
    file.write(shortcut.Targetpath)

elif os.path.islink(sys.argv[1]):
    print('Symbolic Link detected')
    link_target = lib_file_handling.get_symlink_target(sys.argv[1])
    if link_target is not None:
        print(link_target)
        file.write(link_target)

elif lib_file_handling.is_junction(sys.argv[1]):
    print('Junction detected')
    link_target = lib_file_handling.get_junction_target(sys.argv[1])
    print(link_target)
    file.write(link_target)

elif os.stat(sys.argv[1]).st_nlink > 1:
    print('Hard Link detected')
    stdout, stderr, returncode = lib_windows.run_command_line('"' + sys.argv[3] + '" "' + sys.argv[1] + '"')
    findlinks_return = stdout.splitlines()
    if int(findlinks_return[7][8:]) > 0:
        for line in range(10, len(findlinks_return)):
            if findlinks_return[line].lower() != sys.argv[1].lower():
                print(findlinks_return[line])
                file.write(findlinks_return[line] + '\n')

file.close()

And the executable is here https://technet.microsoft.com/en-us/sysinternals/hh290814

Mathias (Author)

  • Administrator
  • VIP Member
  • *****
  • Posts: 4416
    • View Profile
    • Multi Commander
Re: Go To Link Target
« Reply #1 on: February 27, 2017, 17:52:31 »
Check the latest beta, there are MultiScript function in that to get link type and target, it should make the script smaller and easier
http://forum.multicommander.com/forum/index.php/topic,1953.0.html

« Last Edit: February 27, 2017, 17:56:28 by Mathias (Author) »

pncdaspropagandas

  • Contributor
  • Active Member
  • *****
  • Posts: 93
    • View Profile
Re: Go To Link Target
« Reply #2 on: March 13, 2017, 14:34:49 »
This is great! It will make things way more simple and realiable! Thanks Mathias!