Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Remote contol of XYplorer
#4
Please find enclosed an updated version. In case no receiving window is registered, the class itself has now an internal functionality to wait and deliver the response from xyPlorer back to the calling function as result.


Code:
Copy      Help
// class "prXyPlorer.cs"
using System.Runtime.InteropServices;
using System.Text;

using Au;
using Au.More;
using Au.Types;

namespace prGeneral;

public class prXyplorer {

  /// <summary>
  ///
Startup directory of xyPlorer (working directory with scripts).
  /// </summary>
  public static string xyDir = @"C:\Users\Siegfried\AppData\Roaming\XYplorer";

  /// <summary>
  ///
Full path to xyPlorer exe.
  /// </summary>
  public static string xyExe = @"C:\Program Files (x86)\XYplorer\XYplorer.exe";

  private static wnd wndReceiver = default;

  private static wnd defaultReceiver = WndUtil.CreateMessageOnlyWindow(
    static nint (wnd w, int msg, nint wp, nint lp) => {
      switch (msg) {
        case Api.WM_CLIPBOARDUPDATE:
          print.it("WM_CLIPBOARDUPDATE trigger");

          //if want to get clipboard text, do it with a delay, else it may interfere with other clipboard programs or scripts
          //timer.after(100, _ => {
          //    var s = clipboard.text;
          //    print.it(s);
          //});

          break;

        case Api.WM_COPYDATA:

          // SR-COPYDATA receive WM_COPYDATA from external program
          print.it($"WM_COPYDATA trigger {w} {wp} {lp}");
          WndCopyData copyData = new WndCopyData(lp);
          xyResult = copyData.GetString();
          print.it($"WM_COPYDATA received '{xyResult}'");

          break;

        case Api.WM_EXITMENULOOP:
          print.it("WM_EXITMENULOOP trigger");
          break;

        case Api.WM_COMMAND:
          print.it($"WM_COMMAND trigger {w} {wp} {lp}");
          break;

        default:
          print.it($"OtherTriggers {prWindow.MessageName(msg)}={msg}");
          break;
      }


      return Api.DefWindowProc(w, msg, wp, lp);
    },
"#32770");

  private static string xyResult = "";

  private unsafe class Api : NativeApi {


    // CLIPBOARD
    internal const int WM_CLIPBOARDUPDATE = 0x31D;

    internal const int WM_COMMAND = 0x0111;

    internal const int WM_CONTEXTMENU = 0x007B;

    // COPYDATA
    internal const int WM_COPYDATA = 0x004A;

    internal const int WM_DDE_EXECUTE = 0x03E8;

    // DDE
    internal const int WM_DDE_INITIATE = 0x03E0;

    internal const int WM_DDE_TERMINATE = 0x03E1;

    // MENU
    internal const int WM_EXITMENULOOP = 0x0212;


    [
DllImport("user32.dll")]
    internal static extern bool AddClipboardFormatListener(wnd hwnd);

    [
DllImport("user32.dll", EntryPoint = "DefWindowProcW")]
    internal static extern nint DefWindowProc(wnd hWnd, int msg, nint wParam, nint lParam);

  }



  /// <summary>
  ///
Calls XyPlorer Functions via commandline option '/feed=' and get result back via registered wnd message pump using WM_COPYDATA.
  /// </summary>
  ///
<param name="script"> xyPlorer Script to call </param>
  ///
<returns> result string should be empty because result comes back via message pump of receiver-window using WM_COPYDATA </returns>
  public static async Task<string> FeedToXyPlorer(
    string script,
    int waitSecondsForResult = 0
    ) {
    print.it($"FeedToXyPlorer({script})");

    PrepareXyPlorerCall();

    string xyArgs = $"/feed=|{CreateXyPlorerCall(script)}|";
    print.it($"run.console([out] string, {xyExe}, {xyArgs}, {xyDir}, Encoding.UTF8)");
    run.thread(() => {
      int result = run.console(out string xyReturned, xyExe, xyArgs, xyDir, Encoding.UTF8);
    });


    bool timeOut = await WaitForXyPlorerResult(waitSecondsForResult);
    print.it($"timeOut={timeOut} xyResult={xyResult}");

    return xyResult;
  }


  /// <summary>
  ///
Return window of running instance of xyPlorer or starting up new instance.
  /// </summary>
  ///
<returns> Return window of xyPlorer </returns>
  public static wnd GetXyplorer() {
    wnd wndXyplorer = wnd.findOrRun(cn: "ThunderRT6FormDC", run: () => {
      print.it($"start xyplorer");
      run.it(xyExe, dirEtc: xyDir);
    });

    return wndXyplorer;
  }


  /// <summary>
  ///
Register window which should receive result back via WM_COPYDATA from XyPlorerCalls
  /// </summary>
  ///
<param name="receiver"> </param>
  public static void registerReceiverWindow(
    wnd receiver
    ) {
    print.it($"RegisterReceiver({receiver.Handle})");
    wndReceiver = receiver;
  }


  /// <summary>
  ///
Set result from xyPlorer from external receiver
  /// </summary>
  ///
<param name="xyResult"> </param>
  public static void setResultFromReceiver(
    string resultFromReceiver
    ) {
    xyResult = resultFromReceiver;
  }


  /// <summary>
  ///
Call xyPlorer functions and get result back via registered wnd message pump using WM_COPYDATA. Result comes back via message pump of receiver-window using WM_COPYDATA.
  /// </summary>
  ///
<param name="script">               xyPlorer Script to call </param>
  ///
<param name="waitSecondsForResult"> Time in seconds to wait for result from xyPlorer </param>
  ///
<example> prXyplorer.QueryXyPlorer("tab('get', 'path', 1)"); // gets path of first actual tab </example>
  ///
<returns> result string should be empty because </returns>
  public static async Task<string> QueryXyPlorer(
    string script,
    int waitSecondsForResult = 0
    ) {
    wnd w = wnd.findOrRun(of: "xyplorer.exe", run: () => run.it("xyplorer.exe"));
    if (w.Is0) return "";

    PrepareXyPlorerCall();

    WndCopyData.SendReceive<char>(w, 0x00400001, CreateXyPlorerCall(script), out string receivedFromXyPlorer);
    print.it($"receivedFromXyPlorer {receivedFromXyPlorer}");
    if (!string.IsNullOrEmpty(receivedFromXyPlorer)) {
      print.it($"Unexpected data received from xyPlorer {receivedFromXyPlorer}");
    }


    bool timeOut = await WaitForXyPlorerResult(waitSecondsForResult);
    print.it($"timeOut={timeOut} xyResult={xyResult}");

    return xyResult;
  }


  /// <summary>
  ///
Wait for result from xyPlorer. Result is stored in xyResult.
  /// </summary>
  ///
<param name="waitSecondsForResult"> Maximal waiting time in seconds. </param>
  ///
<returns> true if timeout was reached </returns>
  private static async Task<bool> WaitForXyPlorerResult(
    int waitSecondsForResult
    ) {
    return await Task.Run(() => wait.until(-waitSecondsForResult, () => {
      print.it($"WaitForXyPlorerResult #{waitSecondsForResult--}s");
      if (!string.IsNullOrEmpty(xyResult))
        return true;
      return false;
    }));
  }


  /// <summary>
  ///
Reset previous result and register default receiver if extern isnt set already.
  /// </summary>
  private static void PrepareXyPlorerCall() {

    // clear xyResult
    setResultFromReceiver("");

    // need to register default?
    if (wndReceiver.Is0) {
      print.it($"Set default receiver {defaultReceiver.Handle}");
      registerReceiverWindow(defaultReceiver);
    }
  }


  private static string CreateXyPlorerCall(
    string commandlist
    ) {
    string result = commandlist;
    if (!wndReceiver.Is0) {
      result = $"::copydata '{wndReceiver.Handle}', ({commandlist}), 0;";
    }
else {
      print.it($"wndReceiver {wndReceiver} Is0");
    }

    print.it($"CreateXyPlorerCall({commandlist}) = {result}");
    return result;
  }

}


Messages In This Thread
Remote contol of XYplorer - by MBaas - 03-03-2024, 01:40 PM
RE: Remote contol of XYplorer - by AutoStarter - 03-05-2024, 08:25 AM
RE: Remote contol of XYplorer - by MBaas - 03-05-2024, 09:31 AM
Updated version of hlper class prXyPlorer - by AutoStarter - 03-15-2024, 11:13 AM

Forum Jump:


Users browsing this thread: 2 Guest(s)