Страница 44 из 44

Добавлено: Вт сен 08, 2026 21:34
Destiny
Мне казалось слова всегда лучше всего рассказывают о проблеме, потому что я могу внести пояснения туда - куда видео их не вставит.

НО реально, давайте понадеемся, что автор вернется и прочтет тут наши размышления и выложит исходники в доступ.

Добавлено: Ср сен 09, 2026 00:32
Jonmey
Destiny писал(а):автор вернется и прочтет тут наши размышления и выложит исходники в доступ.
Выкладывать исходники из-за того, что у кого-то одного из юзеров согласно его исключительно словесным заявлениям что-то не получается?
Блажен неведающий верящий в такое. Может тогда сразу Гислеру это предложить? (там даже целую паству криворуких можно организовать).
А вообще отличный был бы метод получения исходников нужной программы - заявляешь автору, что его программа кривая и требуешь дать ее исходники.... :lol:

Добавлено: Ср сен 09, 2026 11:34
Orion9
Destiny писал(а):Мне казалось слова всегда лучше всего рассказывают о проблеме, потому что я могу внести пояснения туда - куда видео их не вставит.
Не всегда так. Иногда лучше один раз увидеть, чем сто раз услышать.

Вы говорите, что проблема у вас с любой папкой появляется, ну тогда чем не вариант:
[cut]Изображение[/cut]
Как видите, все работает.
Destiny писал(а):автор вернется и прочтет тут наши размышления и выложит исходники в доступ.
И что это даст? Болван уже расписал все, как там устроено )

[cut]Если проанализировать импорты исполняемого файла NTLinksMaker, вы не увидите там привычную функцию CreateSymbolicLinkW.
Вместо использования высокоуровневого Win32 API автор утилиты (MVV) работает напрямую с низкоуровневой структурой файловой системы NTFS. NTLinksMaker создает символические ссылки и точки соединения (Junctions) вручную через прямую запись данных файловой системы в репарсы.
Процесс создания ссылки изнутри выглядит следующим образом:
## 1. Создание пустого объекта
В зависимости от типа ссылки программа вызывает стандартную функцию CreateFileW (с флагами FILE_FLAG_OPEN_REPARSE_POINT и нужными атрибутами) или CreateDirectoryW. На этом этапе создается обычный «нулевой» файл или пустая папка.
## 2. Формирование буфера данных репарса
В памяти структуры выделяется специальный буфер — REPARSE_DATA_BUFFER. Программа вручную заполняет его заголовки и данные:

* Устанавливается правильный идентификатор — Reparse Tag. Для символических ссылок это константа IOCTL_REPARSE_TAG_SYMLINK (0xA000000C), а для точек соединения — IOCTL_REPARSE_TAG_MOUNT_POINT (0xA0000003).
* Внутрь буфера записываются пути к целевому объекту: как заменяющий путь (Substitute Name, имеющий вид вроде \??\C:\Target\), так и отображаемый путь (Printable Name). [1]

## 3. Запись через DeviceIoControl
Когда буфер сформирован, NTLinksMaker отправляет управляющий код драйверу файловой системы NTFS с помощью функции DeviceIoControl: [2]

DeviceIoControl(
hDevice, // Хэндл созданного файла/папки
FSCTL_SET_REPARSE_POINT, // Управляющий код для установки точки повторного анализа
lpInBuffer, // Сформированный буфер REPARSE_DATA_BUFFER
nInBufferSize, // Размер буфера
...
);

После этого NTFS превращает обычный файл или папку в полноценный системный симлинк. [2, 3]
## Зачем нужен такой сложный путь?
Прямая работа с DeviceIoControl и репарсами дает утилите несколько ключевых преимуществ:

1. Универсальность кода: Архитектура программы построена вокруг низкоуровневых точек повторного анализа (Reparse Points). Одним и тем же методом (просто меняя Reparse Tag в буфере) утилита создает и Junctions, и файловые/папочные симлинки. [4]
2. Точечный контроль: Такой подход позволяет вручную управлять флагами внутри ссылки (например, жестко задавать относительный или абсолютный путь) и исключает влияние некоторых ограничений высокоуровневого API.
3. Кросс-версионность и оптимизация: MVV проектировал плагин NTLinks и утилиту NTLinksMaker для максимальной скорости работы с Total Commander. Чтение и редактирование ссылок в самом плагине происходит через зеркальный метод — FSCTL_GET_REPARSE_POINT. Имея уже готовый код для парсинга и сборки буферов репарсов, автору было проще использовать его и для создания ссылок. [1, 4]

(Примечание: Жесткие ссылки (hardlinks) утилита создает стандартным образом через CreateHardLinkW, так как они не являются репарсами).
Хотите посмотреть на структуру буфера REPARSE_DATA_BUFFER для символических ссылок на C++, или вас интересует, как обойти требование прав администратора при создании ссылок низкоуровневым методом?[/cut]
[cut="Python"]Для того чтобы превратить скрипт в полноценную утилиту с графическим интерфейсом (как у NTLinksMaker), мы можем использовать встроенную в Python библиотеку tkinter.

Этот скрипт объединяет логику низкоуровневой работы с NTFS через DeviceIoControl и простое диалоговое окно для выбора папок.

Код: Выделить всё

import ctypesfrom ctypes import wintypesimport osimport structimport sysimport tkinter as tkfrom tkinter import filedialog, messagebox
# --- Константы Win32 API ---GENERIC_WRITE = 0x40000000OPEN_EXISTING = 3FILE_SHARE_WRITE = 0x00000002FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000FILE_FLAG_BACKUP_SEMANTICS = 0x02000000FSCTL_SET_REPARSE_POINT = 0x000900A4IOCTL_REPARSE_TAG_SYMLINK = 0xA000000CSYMLINK_FLAG_RELATIVE = 0x00000000 
def create_symlink_lowlevel(link_path, target_path):
    """Низкоуровневое создание символической ссылки (метод NTLinksMaker)"""
    os.makedirs(link_path, exist_ok=True)
    
    substitute_name = f"\\??\\{os.path.abspath(target_path)}"
    print_name = os.path.abspath(target_path)
    
    sub_bytes = substitute_name.encode('utf-16le')
    print_bytes = print_name.encode('utf-16le')
    
    sub_len = len(sub_bytes)
    print_len = len(print_bytes)
    
    sub_offset = 0
    print_offset = sub_len
    path_buffer = sub_bytes + print_bytes
    
    reparse_data_length = 12 + len(path_buffer)
    
    header = struct.pack(
        "<IHHHHHHI",
        IOCTL_REPARSE_TAG_SYMLINK,  # ReparseTag
        reparse_data_length,        # ReparseDataLength
        0,                          # Reserved
        sub_offset,                 # SubstituteNameOffset
        sub_len,                    # SubstituteNameLength
        print_offset,               # PrintNameOffset
        print_len,                  # PrintNameLength
        SYMLINK_FLAG_RELATIVE       # Flags
    )
    
    full_buffer = header + path_buffer

    handle = ctypes.windll.kernel32.CreateFileW(
        wintypes.LPCWSTR(link_path),
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        None,
        OPEN_EXISTING,
        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
        None
    )
    
    if handle == -1:
        raise ctypes.WinError(ctypes.get_last_error())
        
    try:
        bytes_returned = wintypes.DWORD(0)
        success = ctypes.windll.kernel32.DeviceIoControl(
            handle,
            FSCTL_SET_REPARSE_POINT,
            full_buffer,
            len(full_buffer),
            None,
            0,
            ctypes.byref(bytes_returned),
            None
        )
        if not success:
            raise ctypes.WinError(ctypes.get_last_error())
    finally:
        ctypes.windll.kernel32.CloseHandle(handle)
# --- Графический интерфейс (GUI) ---class NTLinksApp:
    def __init__(self, root):
        self.root = root
        self.root.title("NTLinksMaker Python Clone")
        self.root.geometry("550x200")
        self.root.resizable(False, False)

        # Переменные путей
        self.target_dir = tk.StringVar()
        self.link_dir = tk.StringVar()

        # Строка 1: Исходная папка (Target)
        tk.Label(root, text="Целевая папка (куда ссылаемся):").grid(row=0, column=0, sticky="w", px=10, py=10)
        tk.Entry(root, textvariable=self.target_dir, width=50).grid(row=0, column=1, py=10)
        tk.Button(root, text="Обзор...", command=self.browse_target).grid(row=0, column=2, px=5, py=10)

        # Строка 2: Папка-ссылка (Link)
        tk.Label(root, text="Имя ссылки (что создаем):").grid(row=1, column=0, sticky="w", px=10, py=10)
        tk.Entry(root, textvariable=self.link_dir, width=50).grid(row=1, column=1, py=10)
        tk.Button(root, text="Обзор...", command=self.browse_link).grid(row=1, column=2, px=5, py=10)

        # Кнопка создания
        tk.Button(root, text="Создать Симлинк (DeviceIoControl)", command=self.process_creation, 
                  bg="#4CAF50", fg="white", font=("Arial", 10, "bold"), height=2).grid(row=2, column=0, columnspan=3, py=20)

    def browse_target(self):
        folder = filedialog.askdirectory(title="Выберите целевую папку")
        if folder:
            self.target_dir.set(os.path.normpath(folder))

    def browse_link(self):
        # Запрашиваем имя будущей папки-ссылки
        folder = filedialog.asksaveasfilename(title="Укажите путь и имя для будущей ссылки", initialfile="NewLink")
        if folder:
            self.link_dir.set(os.path.normpath(folder))

    def process_creation(self):
        tgt = self.target_dir.get()
        lnk = self.link_dir.get()

        if not tgt or not lnk:
            messagebox.showerror("Ошибка", "Заполните оба пути!")
            return

        if not os.path.exists(tgt):
            messagebox.showerror("Ошибка", "Целевая папка не существует!")
            return

        try:
            create_symlink_lowlevel(lnk, tgt)
            messagebox.showinfo("Успех", f"Символическая ссылка успешно создана!\n\n{lnk} -> {tgt}")
        except Exception as e:
            messagebox.showerror("Ошибка Windows API", f"Не удалось создать ссылку.\nВозможно, утилита запущена без прав Администратора.\n\nДетали: {e}")
if __name__ == "__main__":
    # Проверка на запуск с правами Администратора (нужно для создания Symlink в Windows)
    if not ctypes.windll.shell32.IsUserAnAdmin():
        # Перезапускаем скрипт с запросом прав администратора
        ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
        sys.exit()

    root = tk.Tk()
    # Принудительно настраиваем отступы grid для совместимости
    for child in root.winfo_children():
        pass
    
    # Конфигурация сетки для красивых отступов (замена устаревшему px/py во внутреннем синтаксисе)
    app = NTLinksApp(root)
    root.mainloop()

## Как запустить этот скрипт:

   1. Сохраните код в файл, например ntlinks_ui.py.
   2. Запустите его двойным кликом или через консоль.
   3. Скрипт автоматически перезапустится с запросом прав Администратора (появится стандартное окно UAC Windows), так как я добавил проверку IsUserAnAdmin() в блок __main__.
   4. В открывшемся окне выберите исходную папку и путь, где создать новую ссылку.
[/cut]

Добавлено спустя 7 минут:
Jonmey писал(а):Может тогда сразу Гислеру это предложить? (там даже целую паству криворуких можно организовать).
:lol:

[cut]I'm in[/cut]
Jonmey писал(а):А вообще отличный был бы метод получения исходников нужной программы - заявляешь автору, что его программа кривая и требуешь дать ее исходники....
Идеальная схема. Безотказная :lol:

Добавлено: Ср сен 09, 2026 21:12
Destiny
Исходники нужны не просто мне - ибо я ... дальше подставляем всё, на что фантазии хватает.
А потому что вроде всегда в большинстве плагинов исходники БЫЛИ уже в составе и кому надо - могли взять знамя разработки и довести до ума новый функционал. А тут и старый уже не поправишь, и новый не добавить. НУ прям вот повод спрашивать болванов с ИИ о том: как написать свой новый плагин с ... и с ...

Добавлено: Ср сен 09, 2026 23:32
Jonmey
Destiny писал(а):Исходники нужны не просто мне
Купите их у автора и подарите форуму. Точнее, сделайте ему такую донацию, чтобы ему сразу захотелось сделать программу опенсурсной.
И все, наконец, обретут счастье.