0

I just tried to create a small Python script that first installs a certain package (PyMOL), based on the Python version installed on the system and then creates a shortcut to that program on the user's desktop.

The file I want to create a shortcut to, is then located in %APPDATA%/python/pythonVERSION/scripts/pymol.exe.

Step one works flawlessly and installs the package as expected. Step two, however turns out to be quite difficult in native Python.

All solutions I was able to find so far use packages like: win32com, pythoncom, swinlnk, ...

I don't want to have to install packages that I only need once for creating a shortcut on everyone's PC I am trying to install PyMOL.

So is there a way to create a shortcut to a file in native Python, without having to install any sort of 3rd party package?

Just to show some solutions, I already found:

Create shortcut files in Windows 7 using Python

https://www.blog.pythonlibrary.org/2010/01/23/using-python-to-create-shortcuts/

How to create a shortcut to a folder on Windows?

Create shortcut files in Windows 10 using Python 3.7.1

Python, create shortcut with two paths and argument

n00by0815
  • 184
  • 1
  • 12
  • 1
    Perhaps use a PowerShell script? See https://stackoverflow.com/questions/9701840/how-to-create-a-shortcut-using-powershell –  Dec 09 '20 at 21:00
  • Thanks for the answer. That would be a possible solution, just as well as using a .bat file, however my goal was to do it in Python. If this does not work, I'll have to use a different solution anyway. But how hard can it be to create a file and put some information in it, about what it links to? If there is nothing built into Python, I may just try to code that functionality myself. – n00by0815 Dec 09 '20 at 21:10
  • 1
    I see Mike Driscoll's entry in your list. Here are a couple more that might have slightly different approaches: https://pbpython.com/windows-shortcut.html, https://gist.github.com/bitsgalore/7579ab3fecbd4a143feacd1fb44a5858 – RufusVS Dec 09 '20 at 21:18
  • Thanks, I basically already saw those as well. They are both using win32com to create the shortcut in the end. I just discovered: https://github.com/bristi/swinlnk/blob/master/swinlnk/swinlnk.py which I hadn't really looked into too much. It seems to be very little code already, but maybe I can compact it even more, for what I need. I won't need all the checks and so on. I'll try and build a minimum viable solution and if it work well enough, will share it here. – n00by0815 Dec 09 '20 at 21:30
  • https://stackoverflow.com/questions/397125/reading-the-target-of-a-lnk-file-in-python see answers below the accepted one, basically already do, what I need. So there is no need, to implement this myself. I will share my results, once I get around to implementing the code. – n00by0815 Dec 10 '20 at 10:51

1 Answers1

0

To follow up, as I had promised:

most code from: https://github.com/bristi/swinlnk/blob/e4bccd2be6e3e6fd501c627aead5602b132243fd/swinlnk/swinlnk.py

I just simplified it, to accomodate my needs. It would probably be easier, to just install the module, but now it does, what I need it to do. It creates a shortcut on the Desktop of the current user to the default installation path of PyMOL, in my case %APPDATA%/Python/PythonVER/Scripts/pymol.exe.

Code has mostly been simplified, in terms of checking if the shortcut is to a file, folder, or network share, so this only works, if you want to create a shortcut to a FILE on a LOCAL drive.

def ascii2hex(ascii_string):
    data = [format(ord(x), '02x') for x in ascii_string]
    datastring = ''.join(data)
    return datastring
    
def convert_clsid_to_data(clsid):
        slices = [
            (6, 2),
            (4, 2),
            (2, 2),
            (0, 2),
            (11, 2),
            (9, 2),
            (16, 2),
            (14, 2),
            (19, 4),
            (24, 12),
        ]

        data = [clsid[x:x + y] for x, y in slices]
        datastring = ''.join(data)

        return datastring

def gen_idlist(id_item):

    id_item_len = len(id_item) * 2
    item_size = format(int(id_item_len / 4) + 2, '04x')

    slices = [
        (2, 2),
        (0, 2),
    ]

    data = [item_size[x:x + y] for x, y in slices]

    datastring = ''.join(data) + id_item

    return datastring
    
def create_desktop_shortcut(package):
    package = str(package).split('-cp')
    version = package[-2]
    
    convert_clsid_to_data(
          "20d04fe0-3aea-1069-a2d8-08002b30309d"
        )

    PREFIX_LOCAL_ROOT = '2f'

    PREFIX_FILE = '320000000000000000000000'

    item_data = '1f50' + 'e04fd020ea3a6910a2d808002b30309d'

    appdata = Path.home()
    appdata = appdata / "AppData/Roaming"
    
    if len(version) == 2:
        pymol_path = 'Python/Python' + str(version) + '/Scripts/pymol.exe'
        p = PureWindowsPath(appdata / pymol_path)
    else:
        print('NEW VERSION OF PYTHON, please implement shortcut creation.')
        exit

    target_root = p.drive

    if len(p.parts) > 1:
        target_leaf = str(p)[len(p.drive)+1:]
                                        
    type_target = "file"

    file_attributes = F'20000000'

    target_root = ascii2hex(target_root)
    target_root = target_root + ('00' * 21)
    target_leaf = ascii2hex(target_leaf)
    prefix_root = '2f'
    END_OF_STRING = '00'
    prefix_of_target = '320000000000000000000000'

    idlist_items = ''.join([
                    gen_idlist(item_data),
                    gen_idlist(prefix_root + target_root + END_OF_STRING),
                    gen_idlist(
                        prefix_of_target + target_leaf + END_OF_STRING
                    ),
                ])
                
    idlist = gen_idlist(idlist_items)

    pymol_desktop_shortcut = Path.home() / 'Desktop/PyMOL.lnk'
    
    if pymol_desktop_shortcut.is_file():
        exit('Shortcut already exists. Exiting.')

    with open(pymol_desktop_shortcut, 'wb') as fout:
        fout.write(
            binascii.unhexlify(''.join([
                '4c000000',
                '0114020000000000c000000000000046',
                '01010000',
                '20000000',
                '0000000000000000',
                '0000000000000000',
                '0000000000000000',
                '00000000',
                '00000000',
                '01000000',
                '0000',
                '0000',
                '00000000',
                '00000000',
                idlist,
                '0000',
            ]))
        )
```
n00by0815
  • 184
  • 1
  • 12