2021-06-27 21:25:29 +00:00
/***
Copyright ( C ) 2021 J Reece Wilson ( a / k / a " Reece " ) . All rights reserved .
File : Open . Win32 . cpp
Date : 2021 - 6 - 12
Author : Reece
* * */
2021-09-30 14:57:41 +00:00
# include <Source/RuntimeInternal.hpp>
2021-06-27 21:25:29 +00:00
# include "Processes.hpp"
# include "Open.Win32.hpp"
# include <shellapi.h>
# include <tlhelp32.h>
2021-10-23 18:42:05 +00:00
# include <Source/IO/FS/FS.hpp>
# include "Objbase.h"
# include "shellapi.h"
2021-06-27 21:25:29 +00:00
namespace Aurora : : Processes
{
2021-10-23 18:42:05 +00:00
static AuList < AuString > gOpenItems ;
static AuThreadPrimitives : : ConditionMutexUnique_t gCondMutex ;
static AuThreadPrimitives : : ConditionVariableUnique_t gCondVariable ;
2021-11-05 17:34:23 +00:00
static AuThreads : : ThreadUnique_t gOpenerThread ;
2021-10-23 18:42:05 +00:00
static void RunTasks ( )
{
AU_LOCK_GUARD ( gCondMutex ) ;
2022-01-19 02:49:44 +00:00
while ( AuIsThreadRunning ( ) )
2021-10-23 18:42:05 +00:00
{
2021-11-22 16:16:02 +00:00
try
2021-10-23 18:42:05 +00:00
{
2021-11-22 16:16:02 +00:00
for ( const auto & open : gOpenItems )
{
2022-04-09 15:53:14 +00:00
if ( open . empty ( ) )
{
// We probably ran out of memory.
// AuProcess/Open can safely drop as we expect shells to be kinda fucky and async
//
// Case in point: Minecraft on Linux (would?) blocks when you click a link in chat
//
// Fuck tons of applications support clicking of links, in the case of TS and others, allowing for RCE.
// In the case of MC and others, they don't even know if the operation blocks until the process closes.
// Assuming non-blocking, the API returns false on failure; but if it's blocking, who knows what that
// means... Nonzero exit code? Not enough resources? No error?
//
// Websites, programs, and scripts wouldn't know how to process "missing protocol handler,"
// "not enough resources," "process crashed before pump," "shell busy." For the most part, we don't
// expect expect the developer to be aware of what happens after a request to open a resource is
// requested. It's a lot of engineering effort for what should be fork, exec("start", ...)
//
// Dropping invalid paths, out of memory during UTF8 conversion, and other IO issues is probably fine.
// Use an actual IProcess object, if you care about spawning and monitoring executables.
continue ;
}
ShellExecuteW ( nullptr ,
AuIOFS : : DirExists ( open ) ? L " explore " : L " open " ,
Locale : : ConvertFromUTF8 ( open ) . c_str ( ) ,
nullptr ,
nullptr ,
SW_SHOWNORMAL ) ;
2021-11-22 16:16:02 +00:00
}
gOpenItems . clear ( ) ;
gCondVariable - > WaitForSignal ( ) ;
}
catch ( . . . )
{
Debug : : PrintError ( ) ;
2022-01-24 18:37:06 +00:00
AuLogWarn ( " An error occurred while dispatching a ShellExecute runner frame " ) ;
2021-10-23 18:42:05 +00:00
}
}
}
2021-10-23 22:37:18 +00:00
static void OpenerThread ( )
2021-10-23 18:42:05 +00:00
{
2022-04-09 15:53:14 +00:00
CoInitializeEx ( nullptr , COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE ) ;
2021-10-23 18:42:05 +00:00
RunTasks ( ) ;
CoUninitialize ( ) ;
}
void InitWin32Opener ( )
{
gCondMutex = AuThreadPrimitives : : ConditionMutexUnique ( ) ;
2022-01-24 18:37:06 +00:00
SysAssert ( gCondMutex ) ;
2021-10-23 18:42:05 +00:00
gCondVariable = AuThreadPrimitives : : ConditionVariableUnique ( AuUnsafeRaiiToShared ( gCondMutex ) ) ;
2022-01-24 18:37:06 +00:00
SysAssert ( gCondVariable ) ;
2021-11-05 17:34:23 +00:00
gOpenerThread = AuThreads : : ThreadUnique ( AuThreads : : ThreadInfo (
AuMakeShared < AuThreads : : IThreadVectorsFunctional > ( AuThreads : : IThreadVectorsFunctional : : OnEntry_t ( std : : bind ( OpenerThread ) ) ,
AuThreads : : IThreadVectorsFunctional : : OnExit_t { } ) ,
2021-10-23 18:42:05 +00:00
" COM ShellExecute Runner "
) ) ;
2022-01-24 18:37:06 +00:00
SysAssert ( gOpenerThread ) ;
2021-10-23 18:42:05 +00:00
gOpenerThread - > Run ( ) ;
}
void DeinitWin32Opener ( )
{
gOpenerThread - > SendExitSignal ( ) ;
gCondVariable - > Broadcast ( ) ;
gOpenerThread . reset ( ) ;
gCondVariable . reset ( ) ;
gCondMutex . reset ( ) ;
}
2021-06-27 21:25:29 +00:00
AUKN_SYM void OpenUri ( const AuString & uri )
{
2021-10-23 18:42:05 +00:00
AU_LOCK_GUARD ( gCondMutex ) ;
2022-04-09 15:53:14 +00:00
AuTryInsert ( gOpenItems , uri ) ;
2021-10-23 18:42:05 +00:00
gCondVariable - > Broadcast ( ) ;
2021-06-27 21:25:29 +00:00
}
AUKN_SYM void OpenFile ( const AuString & file )
{
2022-01-24 18:37:06 +00:00
OpenUri ( AuIOFS : : NormalizePathRet ( file ) ) ;
2021-06-27 21:25:29 +00:00
}
2022-04-09 15:53:14 +00:00
// TODO: Consider creating blocking apis whose return value is an IProcess (construct from ShellExecuteExW -> in.hProcess, or ("xdg-start", ...))
// For the most part, blocking for a specific application in the context of a protocol or file open request is a dated computing construct.
// Nowdays, opening an editor, mail client, or such like means poking a single executable that'll spawn a fuck ton of background workers, io threads,
// and other resources, to manage multiple instances of whatever the application deals with (think: editor tabs; browser windows; sendto: isnt a modal)
2021-06-27 21:25:29 +00:00
}