mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 14:46:04 +02:00
URI Scheme
This commit is contained in:
@@ -337,6 +337,13 @@ namespace VRCX
|
|||||||
VRCXVR._browser2.ExecuteScriptAsync($"$app.{function}", json);
|
VRCXVR._browser2.ExecuteScriptAsync($"$app.{function}", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetLaunchCommand()
|
||||||
|
{
|
||||||
|
string command = StartupArgs.LaunchCommand;
|
||||||
|
StartupArgs.LaunchCommand = string.Empty;
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetStartup(bool enabled)
|
public void SetStartup(bool enabled)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright(c) 2019-2021 pypy. All rights reserved.
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
using CefSharp;
|
||||||
|
using System;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace VRCX
|
||||||
|
{
|
||||||
|
class IPCClient
|
||||||
|
{
|
||||||
|
private NamedPipeServerStream _ipcServer;
|
||||||
|
private byte[] _recvBuffer = new byte[1024 * 8];
|
||||||
|
|
||||||
|
private string _currentPacket;
|
||||||
|
|
||||||
|
public IPCClient(NamedPipeServerStream ipcServer)
|
||||||
|
{
|
||||||
|
_ipcServer = ipcServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginRead()
|
||||||
|
{
|
||||||
|
_ipcServer.BeginRead(_recvBuffer, 0, _recvBuffer.Length, OnRead, _ipcServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRead(IAsyncResult asyncResult)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var bytesRead = _ipcServer.EndRead(asyncResult);
|
||||||
|
|
||||||
|
if (bytesRead <= 0)
|
||||||
|
{
|
||||||
|
_ipcServer.Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPacket += Encoding.UTF8.GetString(_recvBuffer, 0, bytesRead);
|
||||||
|
|
||||||
|
if (_currentPacket[_currentPacket.Length - 1] == (char)0x00)
|
||||||
|
{
|
||||||
|
var packets = _currentPacket.Split((char)0x00);
|
||||||
|
|
||||||
|
foreach (var packet in packets)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(packet))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MainForm.Instance.Browser.ExecuteScriptAsync("$app.ipcEvent", packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPacket = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
BeginRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OnSend(IAsyncResult asyncResult)
|
||||||
|
{
|
||||||
|
var ipcClient = (NamedPipeClientStream)asyncResult.AsyncState;
|
||||||
|
ipcClient.EndWrite(asyncResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright(c) 2019-2021 pypy. All rights reserved.
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
|
||||||
|
namespace VRCX
|
||||||
|
{
|
||||||
|
class IPCServer
|
||||||
|
{
|
||||||
|
public static readonly IPCServer Instance;
|
||||||
|
|
||||||
|
static IPCServer()
|
||||||
|
{
|
||||||
|
Instance = new IPCServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
new IPCServer().CreateIPCServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateIPCServer()
|
||||||
|
{
|
||||||
|
var ipcServer = new NamedPipeServerStream("vrcx-ipc", PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
||||||
|
ipcServer.BeginWaitForConnection(asyncResult => DoAccept(asyncResult), ipcServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoAccept(IAsyncResult asyncResult)
|
||||||
|
{
|
||||||
|
var ipcServer = (NamedPipeServerStream)asyncResult.AsyncState;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ipcServer.EndWaitForConnection(asyncResult);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
new IPCClient(ipcServer).BeginRead();
|
||||||
|
CreateIPCServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -145,6 +145,14 @@ Section "Install" SecInstall
|
|||||||
CreateShortCut "$SMPROGRAMS\VRCX.lnk" "$INSTDIR\VRCX.exe"
|
CreateShortCut "$SMPROGRAMS\VRCX.lnk" "$INSTDIR\VRCX.exe"
|
||||||
ApplicationID::Set "$SMPROGRAMS\VRCX.lnk" "VRCX"
|
ApplicationID::Set "$SMPROGRAMS\VRCX.lnk" "VRCX"
|
||||||
|
|
||||||
|
WriteRegStr HKCU "Software\Classes\vrcx" "" "URL:vrcx"
|
||||||
|
WriteRegStr HKCU "Software\Classes\vrcx" "FriendlyTypeName" "VRCX"
|
||||||
|
WriteRegStr HKCU "Software\Classes\vrcx" "URL Protocol" ""
|
||||||
|
WriteRegExpandStr HKCU "Software\Classes\vrcx\DefaultIcon" "" "$INSTDIR\VRCX.ico"
|
||||||
|
WriteRegStr HKCU "Software\Classes\vrcx\shell" "" "open"
|
||||||
|
WriteRegStr HKCU "Software\Classes\vrcx\shell\open" "FriendlyAppName" "VRCX"
|
||||||
|
WriteRegStr HKCU "Software\Classes\vrcx\shell\open\command" "" '"D:\WindowsFiles\Documents\git\VRCX\bin\x64\Release\VRCX.exe" /uri="%1" /params="%2 %3 %4"'
|
||||||
|
|
||||||
${If} ${Silent}
|
${If} ${Silent}
|
||||||
SetOutPath $INSTDIR
|
SetOutPath $INSTDIR
|
||||||
ShellExecAsUser::ShellExecAsUser "" "$INSTDIR\VRCX.exe" ""
|
ShellExecAsUser::ShellExecAsUser "" "$INSTDIR\VRCX.exe" ""
|
||||||
@@ -169,6 +177,7 @@ Section "Uninstall"
|
|||||||
|
|
||||||
DeleteRegKey HKLM "Software\VRCX"
|
DeleteRegKey HKLM "Software\VRCX"
|
||||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VRCX"
|
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VRCX"
|
||||||
|
DeleteRegKey HKCU "Software\Classes\vrcx"
|
||||||
|
|
||||||
Delete "$SMPROGRAMS\VRCX.lnk"
|
Delete "$SMPROGRAMS\VRCX.lnk"
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ namespace VRCX
|
|||||||
|
|
||||||
private static void Run()
|
private static void Run()
|
||||||
{
|
{
|
||||||
|
StartupArgs.ArgsCheck();
|
||||||
Update.Check();
|
Update.Check();
|
||||||
|
|
||||||
Application.EnableVisualStyles();
|
Application.EnableVisualStyles();
|
||||||
@@ -66,6 +67,7 @@ namespace VRCX
|
|||||||
LogWatcher.Instance.Init();
|
LogWatcher.Instance.Init();
|
||||||
|
|
||||||
CefService.Instance.Init();
|
CefService.Instance.Init();
|
||||||
|
IPCServer.Instance.Init();
|
||||||
VRCXVR.Instance.Init();
|
VRCXVR.Instance.Init();
|
||||||
Application.Run(new MainForm());
|
Application.Run(new MainForm());
|
||||||
WebApi.Instance.SaveCookies();
|
WebApi.Instance.SaveCookies();
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
// Copyright(c) 2019-2021 pypy. All rights reserved.
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace VRCX
|
||||||
|
{
|
||||||
|
class StartupArgs
|
||||||
|
{
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool ShowWindow(IntPtr hWnd, int flags);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern int SetForegroundWindow(IntPtr hwnd);
|
||||||
|
|
||||||
|
public static string LaunchCommand;
|
||||||
|
|
||||||
|
public static void ArgsCheck()
|
||||||
|
{
|
||||||
|
string[] args = Environment.GetCommandLineArgs();
|
||||||
|
if (args.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (string arg in args)
|
||||||
|
{
|
||||||
|
if (arg.Length > 12 && arg.Substring(0, 12) == "/uri=vrcx://")
|
||||||
|
{
|
||||||
|
LaunchCommand = arg.Substring(12);
|
||||||
|
ProcessCheck(LaunchCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessCheck(string command)
|
||||||
|
{
|
||||||
|
Process currentProcess = Process.GetCurrentProcess();
|
||||||
|
Process[] processList = Process.GetProcessesByName("VRCX");
|
||||||
|
|
||||||
|
if (processList.Length > 1)
|
||||||
|
{
|
||||||
|
new IPCServer().CreateIPCServer();
|
||||||
|
var ipcClient = new NamedPipeClientStream(".", "vrcx-ipc", PipeDirection.InOut);
|
||||||
|
ipcClient.Connect();
|
||||||
|
|
||||||
|
if (ipcClient.IsConnected)
|
||||||
|
{
|
||||||
|
var buffer = Encoding.UTF8.GetBytes($"{{\"type\":\"LaunchCommand\",\"command\":\"{command}\"}}" + (char)0x00);
|
||||||
|
ipcClient.BeginWrite(buffer, 0, buffer.Length, IPCClient.OnSend, ipcClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Process process in processList)
|
||||||
|
{
|
||||||
|
if (process.Id != currentProcess.Id)
|
||||||
|
{
|
||||||
|
IntPtr handle = process.MainWindowHandle;
|
||||||
|
ShowWindow(handle, 9);
|
||||||
|
SetForegroundWindow(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,6 +81,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="AssetBundleCacher.cs" />
|
<Compile Include="AssetBundleCacher.cs" />
|
||||||
|
<Compile Include="IPCClient.cs" />
|
||||||
|
<Compile Include="IPCServer.cs" />
|
||||||
|
<Compile Include="StartupArgs.cs" />
|
||||||
<Compile Include="Update.cs" />
|
<Compile Include="Update.cs" />
|
||||||
<Compile Include="CefService.cs" />
|
<Compile Include="CefService.cs" />
|
||||||
<Compile Include="Discord.cs" />
|
<Compile Include="Discord.cs" />
|
||||||
|
|||||||
@@ -15582,6 +15582,53 @@ speechSynthesis.getVoices();
|
|||||||
return user.currentAvatarImageUrl;
|
return user.currentAvatarImageUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$app.methods.ipcEvent = function (json) {
|
||||||
|
try {
|
||||||
|
var data = JSON.parse(json);
|
||||||
|
} catch {
|
||||||
|
console.error(`IPC invalid JSON, ${json}`);
|
||||||
|
}
|
||||||
|
switch (data.type) {
|
||||||
|
case 'Ping':
|
||||||
|
this.eventPing(data);
|
||||||
|
break;
|
||||||
|
case 'LaunchCommand':
|
||||||
|
this.eventLaunchCommand(data.command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
API.$on('LOGIN', async function () {
|
||||||
|
var command = await AppApi.GetLaunchCommand();
|
||||||
|
if (command) {
|
||||||
|
$app.eventLaunchCommand(command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$app.methods.eventLaunchCommand = function (command) {
|
||||||
|
if (!API.isLoggedIn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var args = command.split('/');
|
||||||
|
var command = args[0];
|
||||||
|
var commandArg = args[1];
|
||||||
|
switch (command) {
|
||||||
|
case 'world':
|
||||||
|
this.showWorldDialog(commandArg);
|
||||||
|
break;
|
||||||
|
case 'avatar':
|
||||||
|
this.showAvatarDialog(commandArg);
|
||||||
|
break;
|
||||||
|
case 'user':
|
||||||
|
this.showUserDialog(commandArg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$app.methods.eventPing = function (data) {
|
||||||
|
console.log('IPC Ping', data.version);
|
||||||
|
};
|
||||||
|
|
||||||
$app = new Vue($app);
|
$app = new Vue($app);
|
||||||
window.$app = $app;
|
window.$app = $app;
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user