Как запустить документ в ассоциированной программе?

Russian main discussion
Post Reply
  • Author
  • Message
Offline
Posts: 348
Joined: Mon Jun 03, 2019 2:33 am

Как запустить документ в ассоциированной программе?

Post by AZJIO »

Как использовать ShellExecute (ShellExecuteEx), чтобы открыть файл-документ в ассоциированной программе или использовать AssocQueryString, чтобы получить путь к программе и далее использовать его с путём файла? Или FindExecutable, ведь у нас пути существующие.

Кажется нашёл

Code: Select all

var oSys     = AkelPad.SystemFunction();
var hEditWnd = AkelPad.GetEditWnd();

sFile = GetAssocString(".txt")
AkelPad.MessageBox(hEditWnd, sFile, "Резльтат", 0);


function GetAssocString(sStr)
{
  var sFile  = "";
  var lpFile = AkelPad.MemAlloc(260 * 2);;
  var lpSize = AkelPad.MemAlloc(4);

  AkelPad.MemCopy(lpSize, 260, 3 /*DT_DWORD*/);

  if (oSys.Call("Shlwapi::AssocQueryStringW", 0, 2 /*ASSOCSTR_EXECUTABLE*/, sStr, "open", lpFile, lpSize) == 0 /*S_OK*/)
    sFile = AkelPad.MemRead(lpFile, 1 /*DT_UNICODE*/);

  AkelPad.MemFree(lpFile);
  AkelPad.MemFree(lpSize);

  return sFile;
}


И как результат

Code: Select all

var oSys     = AkelPad.SystemFunction();
var hEditWnd = AkelPad.GetEditWnd();

var pEditFile = AkelPad.GetEditFile(0);
var pFileExt  = AkelPad.GetFilePath(pEditFile, 4 /*CPF_FILEEXT*/);
// var sFileExt  = (pFileExt.toLowerCase())
var sFile = GetAssocString("." + pFileExt)


switch (pFileExt.toLowerCase())
{
  case "bat":
  case "cmd":
    AkelPad.Exec('%comspec% /c "' + pEditFile + '"'); // куда без костылей
    break;
  default:
    // для "reg" нужны права админа, тогда работает
    // AkelPad.MessageBox(hEditWnd, sFile + ' "' + pEditFile + '"', "Результат", 0); // посмотреть ком-строку на ошибки
    AkelPad.Exec(sFile + ' "' + pEditFile + '"');
    break;
}


function GetAssocString(sStr)
{
  var sFile  = "";
  var lpFile = AkelPad.MemAlloc(260 * 2);;
  var lpSize = AkelPad.MemAlloc(4);

  AkelPad.MemCopy(lpSize, 260, 3 /*DT_DWORD*/);

  if (oSys.Call("Shlwapi::AssocQueryStringW", 0, 2 /*ASSOCSTR_EXECUTABLE*/, sStr, "open", lpFile, lpSize) == 0 /*S_OK*/)
    sFile = AkelPad.MemRead(lpFile, 1 /*DT_UNICODE*/);

  AkelPad.MemFree(lpFile);
  AkelPad.MemFree(lpSize);

  return sFile;
}

И тут проблема, для запуска прог требующих админ-права команда запуска не выполняется, хотя ожидается запуск с подтверждением запуска от админа, но не происходит ничего. Запускаю AkelPad от админа и всё работает.


Итак новая версия

Code: Select all

var pEditFile = AkelPad.GetEditFile(0);
Shell32_ShellExecuteEx(pEditFile)

function Shell32_ShellExecuteEx(sFile)
{
  var nInfoSize = _X64 ? 112 : 60; //sizeof(SHELLEXECUTEINFO)
  var lpInfo    = AkelPad.MemAlloc(nInfoSize);
  var lpFile    = AkelPad.MemAlloc((sFile.length + 1) * _TSIZE);
  // var bSuccess  = false;

  AkelPad.MemCopy(lpFile, sFile, _TSTR);

  AkelPad.MemCopy(lpInfo, nInfoSize, 3 /*DT_DWORD*/); //cbSize
  AkelPad.MemCopy(_PtrAdd(lpInfo, 4), 0x00000040 /*SEE_MASK_NOCLOSEPROCESS*/, 3 /*DT_DWORD*/); //fMask
  AkelPad.MemCopy(_PtrAdd(lpInfo, _X64 ? 24 : 16), lpFile, 2 /*DT_QWORD*/); //lpFile
  AkelPad.MemCopy(_PtrAdd(lpInfo, _X64 ? 48 : 28), 1 /*SW_SHOWNORMAL*/, 3 /*DT_DWORD*/); //nShow

  AkelPad.SystemFunction().Call("Shell32::ShellExecuteEx" + _TCHAR, lpInfo)
  /*
  // Не будем проверять успех
  if (AkelPad.SystemFunction().Call("Shell32::ShellExecuteEx" + _TCHAR, lpInfo))
    bSuccess = true;
  */

  AkelPad.MemFree(lpInfo);
  AkelPad.MemFree(lpFile);

  // return bSuccess;
}
с использованием ShellExecuteEx. Взял отсюда, там не совсем нужное - для свойств файла, но я переделал под запуск файла, теперь работают и reg и bat без всяких ухищрений. Возможно так как я удалил поле lpVerb в структуре, то размер её должен быть уменьшен, но по крайней мере вреда от этого нет.

Используя "lpVerb" мы можем указать пункт в реестре, например edit (Изменить) вместо open (Открыть)

Code: Select all

var pEditFile = AkelPad.GetEditFile(0);
var sFile1 = Shell32_ShellExecuteEx(pEditFile)


function Shell32_ShellExecuteEx(sFile)
{
  var sVerb     = "edit";
  var nInfoSize = _X64 ? 112 : 60; //sizeof(SHELLEXECUTEINFO)
  var lpInfo    = AkelPad.MemAlloc(nInfoSize);
  var lpVerb    = AkelPad.MemAlloc((sVerb.length + 1) * _TSIZE);
  var lpFile    = AkelPad.MemAlloc((sFile.length + 1) * _TSIZE);
  var bSuccess  = false;

  AkelPad.MemCopy(lpVerb, sVerb, _TSTR);
  AkelPad.MemCopy(lpFile, sFile, _TSTR);

  AkelPad.MemCopy(lpInfo, nInfoSize, 3 /*DT_DWORD*/); //cbSize
  AkelPad.MemCopy(_PtrAdd(lpInfo, 4), 0x00000040 /*SEE_MASK_NOCLOSEPROCESS*/, 3 /*DT_DWORD*/); //fMask
  /*
  if (hWnd)
    AkelPad.MemCopy(_PtrAdd(lpInfo, 8), hWnd, 3 DT_DWORD); //hwnd
  */
  AkelPad.MemCopy(_PtrAdd(lpInfo, _X64 ? 16 : 12), lpVerb, 2 /*DT_QWORD*/); //lpVerb
  AkelPad.MemCopy(_PtrAdd(lpInfo, _X64 ? 24 : 16), lpFile, 2 /*DT_QWORD*/); //lpFile
  AkelPad.MemCopy(_PtrAdd(lpInfo, _X64 ? 48 : 28), 1 /*SW_SHOWNORMAL*/, 3 /*DT_DWORD*/); //nShow

  if (AkelPad.SystemFunction().Call("Shell32::ShellExecuteEx" + _TCHAR, lpInfo))
    bSuccess = true;

  AkelPad.MemFree(lpInfo);
  AkelPad.MemFree(lpVerb);
  AkelPad.MemFree(lpFile);

  return bSuccess;
}

Кстати, это можно сделать через меню вкладки -> Меню проводника.

Offline
Posts: 3234
Joined: Wed Nov 29, 2006 1:19 pm
Location: Киев, Русь
Contact:

Post by VladSh »

Всё проще гораздо.
Настройки -> Общие -> Выполнить -> в поле "Команда" вбиваем:

Code: Select all

rundll32.exe shell32,ShellExec_RunDLL "%f"
а в поле "Каталог":

Code: Select all

%d
и файлы начинают открываться в ассоциированной программе по Ctrl+F5.

Вероятно Вы не читали Дополнения к FAQ.

Offline
Posts: 348
Joined: Mon Jun 03, 2019 2:33 am

Post by AZJIO »

Мой вариант для reg-файлов работает лучше, выводит на передний план, а по закрытию возвращает активность AkelPad`у, вместо открытия на заднем плане с мигающей кнопкой на панели задач, а при отмене не возвращает активность AkelPad`у.

Offline
Posts: 348
Joined: Mon Jun 03, 2019 2:33 am

Post by AZJIO »

Улучшение: так как при запуске js-файлов тупо запускалось внешней прогой с напоминанием об ошибке и бессмысленностью выполнения, теперь это работает как запуск в контексте запуска js-скриптов внутри AkelPad, то есть фактически происходит тест редактируемого в данный момент скрипта, без поиска в меню "Запуск последнего" и без открытия списка всех ваших скриптов в GUI и поиска его имени там, просто выбрал вкладку нажал F5. По умолчанию F5 - переоткрыть файл, но лично я ни разу не пользовался этим пунктом за многие года использования Notepad++, во первых при внешнем изменении он сам переоткрывается, а внутри жмёшь отмена действий, поэтому F5 у меня всегда был запуск файла в ассоциированной программе. Чтобы этот скрипт не запускал сам себя в цикле (миллиард раз пока включен комп мучая проц и hdd) добавил игнор на его имя.

Code: Select all

// AZJIO
var pEditFile = AkelPad.GetEditFile(0);
var pFileExt  = AkelPad.GetFilePath(pEditFile, 4 /*CPF_FILEEXT*/);
switch (pFileExt.toLowerCase()) {
  case "js":
    pFileName = AkelPad.GetFilePath(pEditFile, 2 /*CPF_FILENAME*/)
    if (pFileName !== WScript.ScriptName) // самого себя не запускаем иначе это бесконечный цикл запусков
      AkelPad.Call("Scripts::Main", 1, pFileName);
    break;
  default:
    Shell32_ShellExecuteEx(pEditFile)
    break;
};


function Shell32_ShellExecuteEx(sFile)
{
  var nInfoSize = _X64 ? 112 : 60; //sizeof(SHELLEXECUTEINFO)
  var lpInfo    = AkelPad.MemAlloc(nInfoSize);
  var lpFile    = AkelPad.MemAlloc((sFile.length + 1) * _TSIZE);

  AkelPad.MemCopy(lpFile, sFile, _TSTR);

  AkelPad.MemCopy(lpInfo, nInfoSize, 3 /*DT_DWORD*/); //cbSize
  AkelPad.MemCopy(_PtrAdd(lpInfo, 4), 0x00000040 /*SEE_MASK_NOCLOSEPROCESS*/, 3 /*DT_DWORD*/); //fMask
  AkelPad.MemCopy(_PtrAdd(lpInfo, _X64 ? 24 : 16), lpFile, 2 /*DT_QWORD*/); //lpFile
  AkelPad.MemCopy(_PtrAdd(lpInfo, _X64 ? 48 : 28), 1 /*SW_SHOWNORMAL*/, 3 /*DT_DWORD*/); //nShow

  AkelPad.SystemFunction().Call("Shell32::ShellExecuteEx" + _TCHAR, lpInfo)

  AkelPad.MemFree(lpInfo);
  AkelPad.MemFree(lpFile);
}
Post Reply