336 lines
12 KiB
ObjectPascal
336 lines
12 KiB
ObjectPascal
unit MainUnit;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
|
|
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls, FMX.WebBrowser,
|
|
Winapi.WebView2, FMX.Memo.Types, FMX.ScrollBox, FMX.Memo, FMX.Edit, FMX.StdActns,
|
|
// FMX.Edit für TEdit, FMX.StdActns oft für Standardaktionen
|
|
// Korrekte und vollständige Units
|
|
System.IOUtils, System.Threading, System.Diagnostics, Winapi.Windows, LLVM.Runner;
|
|
|
|
const
|
|
// Pfad zur HTML-Datei (BITTE HIER ANPASSEN!)
|
|
PATH_BLOCKLY = 'T:\Myc\Blockly\index.html'; // <--- PRÜFEN UND ANPASSEN!
|
|
|
|
// Pfade zu den LLVM-Werkzeugen (BITTE HIER ANPASSEN!)
|
|
CLANG_PATH = 'clang.exe'; // <--- PRÜFEN UND ANPASSEN!
|
|
LLD_PATH = 'lld-link.exe'; // <--- PRÜFEN UND ANPASSEN!
|
|
|
|
type
|
|
TMainForm = class( TForm )
|
|
WebBrowser1: TWebBrowser;
|
|
SaveWorkspaceButton: TSpeedButton;
|
|
GenerateCodeButton: TSpeedButton;
|
|
Memo: TMemo;
|
|
InputEdit: TEdit; // <--- Hinzugefügt (Optional für Beschriftung des InputEdit)
|
|
procedure FormCreate( Sender: TObject );
|
|
procedure FormDestroy( Sender: TObject );
|
|
procedure RefreshButtonClick( Sender: TObject );
|
|
procedure SaveWorkspaceButtonClick( Sender: TObject );
|
|
procedure GenerateCodeButtonClick( Sender: TObject );
|
|
procedure WebBrowser1DidFinishLoad( ASender: TObject );
|
|
private
|
|
{ Private declarations }
|
|
FWebView: ICoreWebView2;
|
|
FWebMessageReceiver: ICoreWebView2WebMessageReceivedEventHandler;
|
|
FWebMessageReceivedToken: EventRegistrationToken;
|
|
procedure HandleWebMessage( const AMessage: string );
|
|
function ExecuteProcess( const ACommand: string; const AParameters: string ): string;
|
|
public
|
|
{ Public declarations }
|
|
end;
|
|
|
|
var
|
|
MainForm: TMainForm;
|
|
|
|
implementation
|
|
|
|
{$R *.fmx}
|
|
|
|
|
|
type
|
|
TWebMessageReceiver = class( TInterfacedObject, ICoreWebView2WebMessageReceivedEventHandler )
|
|
private
|
|
FOwner: TMainForm;
|
|
public
|
|
constructor Create( AOwner: TMainForm );
|
|
function Invoke( const Sender: ICoreWebView2; const args: ICoreWebView2WebMessageReceivedEventArgs ): HResult; stdcall;
|
|
end;
|
|
|
|
constructor TWebMessageReceiver.Create( AOwner: TMainForm );
|
|
begin
|
|
inherited Create;
|
|
FOwner := AOwner;
|
|
end;
|
|
|
|
function TWebMessageReceiver.Invoke( const Sender: ICoreWebView2; const args: ICoreWebView2WebMessageReceivedEventArgs ): HResult;
|
|
var
|
|
message: PWideChar;
|
|
begin
|
|
Result := args.TryGetWebMessageAsString( message );
|
|
if Succeeded( Result ) then
|
|
begin
|
|
TThread.Queue( nil,
|
|
procedure
|
|
begin
|
|
FOwner.HandleWebMessage( string( message ) );
|
|
end );
|
|
end;
|
|
Result := S_OK;
|
|
end;
|
|
|
|
procedure TMainForm.FormCreate( Sender: TObject );
|
|
begin
|
|
TLLVMRunner.Log := Memo.Lines;
|
|
// Optional: Standardwert für InputEdit
|
|
InputEdit.Text := '123';
|
|
end;
|
|
|
|
procedure TMainForm.FormDestroy( Sender: TObject );
|
|
begin
|
|
TLLVMRunner.Log := nil;
|
|
if ( FWebView <> nil ) and ( FWebMessageReceivedToken.value <> 0 ) then
|
|
begin
|
|
FWebView.remove_WebMessageReceived( FWebMessageReceivedToken );
|
|
end;
|
|
end;
|
|
|
|
function TMainForm.ExecuteProcess( const ACommand: string; const AParameters: string ): string;
|
|
var
|
|
sa: TSecurityAttributes;
|
|
si: TStartupInfo;
|
|
pi: TProcessInformation;
|
|
stdOutRead, stdOutWrite: THandle;
|
|
success: Boolean;
|
|
cmdLine: string;
|
|
buffer: TBytes;
|
|
bytesRead: Cardinal;
|
|
output, errors: string;
|
|
begin
|
|
// Pipe für StdOut und StdErr erstellen
|
|
sa.nLength := SizeOf( TSecurityAttributes );
|
|
sa.lpSecurityDescriptor := nil;
|
|
sa.bInheritHandle := True;
|
|
|
|
// Für stdout und stderr wird dieselbe Pipe verwendet, da clang Fehler auf stdout ausgibt
|
|
if not CreatePipe( stdOutRead, stdOutWrite, @sa, 0 ) then
|
|
Exit( 'Error creating pipe.' );
|
|
|
|
try
|
|
// StartupInfo vorbereiten
|
|
FillChar( si, SizeOf( TStartupInfo ), 0 );
|
|
si.cb := SizeOf( TStartupInfo );
|
|
si.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
|
|
si.wShowWindow := SW_HIDE; // Fenster explizit verstecken
|
|
si.hStdInput := GetStdHandle( STD_INPUT_HANDLE );
|
|
si.hStdOutput := stdOutWrite;
|
|
si.hStdError := stdOutWrite; // Leite stderr auf dieselbe Pipe wie stdout
|
|
|
|
cmdLine := Format( '"%s" %s', [ACommand, AParameters] );
|
|
|
|
success := CreateProcess(
|
|
nil,
|
|
PChar( cmdLine ),
|
|
nil,
|
|
nil,
|
|
True,
|
|
CREATE_NO_WINDOW,
|
|
nil,
|
|
nil,
|
|
si,
|
|
pi
|
|
);
|
|
|
|
// Schreib-Handle der Pipe sofort schließen
|
|
CloseHandle( stdOutWrite );
|
|
|
|
if not success then
|
|
Exit( Format( 'CreateProcess failed. Code: %d', [GetLastError] ) );
|
|
|
|
try
|
|
output := '';
|
|
// Warten, bis der Prozess fertig ist und alle Daten in die Pipe geschrieben hat
|
|
if WaitForSingleObject( pi.hProcess, 5000 ) = WAIT_TIMEOUT then // 5s Timeout
|
|
begin
|
|
TerminateProcess( pi.hProcess, 1 );
|
|
Exit( 'Process timed out.' );
|
|
end;
|
|
|
|
// Bytes aus der Pipe lesen
|
|
repeat
|
|
SetLength( buffer, 1024 );
|
|
bytesRead := 0;
|
|
if ReadFile( stdOutRead, buffer[0], Length( buffer ), bytesRead, nil ) and ( bytesRead > 0 ) then
|
|
begin
|
|
SetLength( buffer, bytesRead );
|
|
// Gelesene Bytes mit dem Default-System-Encoding in einen String umwandeln
|
|
output := output + TEncoding.Default.GetString( buffer );
|
|
end
|
|
else
|
|
begin
|
|
break; // Pipe ist leer oder Fehler
|
|
end;
|
|
until False;
|
|
|
|
finally
|
|
CloseHandle( pi.hProcess );
|
|
CloseHandle( pi.hThread );
|
|
end;
|
|
|
|
Result := output;
|
|
|
|
finally
|
|
CloseHandle( stdOutRead );
|
|
end;
|
|
end;
|
|
|
|
procedure TMainForm.RefreshButtonClick( Sender: TObject );
|
|
begin
|
|
WebBrowser1.Navigate( PATH_BLOCKLY );
|
|
end;
|
|
|
|
procedure TMainForm.SaveWorkspaceButtonClick( Sender: TObject );
|
|
begin
|
|
WebBrowser1.EvaluateJavaScript( 'saveWorkspace()' );
|
|
end;
|
|
|
|
procedure TMainForm.GenerateCodeButtonClick( Sender: TObject );
|
|
begin
|
|
WebBrowser1.EvaluateJavaScript( 'generateAndPostCode()' );
|
|
end;
|
|
|
|
procedure TMainForm.HandleWebMessage( const AMessage: string );
|
|
var
|
|
llFile, objFile, dllFile: string;
|
|
compilerOutput: string;
|
|
success: Boolean;
|
|
InputInteger: Integer; // <--- Hinzugefügt
|
|
begin
|
|
Memo.Lines.Clear;
|
|
Memo.Lines.Add( 'LLVM-Code empfangen. Starte Kompilierung...' );
|
|
Memo.Lines.Add( AMessage );
|
|
Memo.Lines.Add( '--------------------' );
|
|
|
|
// Wert aus dem InputEdit-Feld holen
|
|
try
|
|
InputInteger := StrToIntDef( InputEdit.Text, 0 ); // Standardwert 0, falls ungültig
|
|
except
|
|
InputInteger := 0;
|
|
Memo.Lines.Add( 'WARNUNG: Ungültige Eingabe im InputEdit-Feld. Verwende 0.' );
|
|
end;
|
|
|
|
TTask.Run(
|
|
procedure
|
|
var
|
|
llFileLocal, objFileLocal, dllFileLocal: string; // Lokale Variablen für den Task
|
|
compilerOutputLocal: string;
|
|
successLocal: Boolean;
|
|
begin
|
|
llFileLocal := TPath.Combine( TPath.GetTempPath, 'poc.ll' );
|
|
objFileLocal := TPath.ChangeExtension( llFileLocal, '.obj' );
|
|
dllFileLocal := TPath.ChangeExtension( llFileLocal, '.dll' );
|
|
successLocal := False;
|
|
|
|
try
|
|
TFile.WriteAllText( llFileLocal, AMessage );
|
|
|
|
TThread.Queue( nil,
|
|
procedure
|
|
begin
|
|
Memo.Lines.Add( 'Kompiliere zu Objektdatei...' );
|
|
end );
|
|
compilerOutputLocal := ExecuteProcess( CLANG_PATH, Format( '-c "%s" -o "%s"', [llFileLocal, objFileLocal] ) );
|
|
TThread.Queue( nil,
|
|
procedure
|
|
begin
|
|
Memo.Lines.Add( compilerOutputLocal );
|
|
end );
|
|
|
|
if not TFile.Exists( objFileLocal ) then
|
|
begin
|
|
TThread.Queue( nil,
|
|
procedure
|
|
begin
|
|
Memo.Lines.Add( 'FEHLER: Kompilierung fehlgeschlagen. .obj-Datei nicht erstellt.' );
|
|
end );
|
|
Exit;
|
|
end;
|
|
|
|
TThread.Queue( nil,
|
|
procedure
|
|
begin
|
|
Memo.Lines.Add( 'Linke zu DLL...' );
|
|
end );
|
|
compilerOutputLocal := ExecuteProcess( LLD_PATH, Format( '/dll /noentry "%s" /out:"%s"', [objFileLocal, dllFileLocal] ) );
|
|
TThread.Queue( nil,
|
|
procedure
|
|
begin
|
|
Memo.Lines.Add( compilerOutputLocal );
|
|
end );
|
|
|
|
if TFile.Exists( dllFileLocal ) then
|
|
begin
|
|
successLocal := True;
|
|
TThread.Queue( nil,
|
|
procedure
|
|
begin
|
|
Memo.Lines.Add( Format( 'ERFOLG: DLL wurde erstellt: %s', [dllFileLocal] ) );
|
|
end );
|
|
end
|
|
else
|
|
begin
|
|
TThread.Queue( nil,
|
|
procedure
|
|
begin
|
|
Memo.Lines.Add( 'FEHLER: Linken fehlgeschlagen. .dll-Datei nicht erstellt.' );
|
|
end );
|
|
end;
|
|
|
|
finally
|
|
if TFile.Exists( llFileLocal ) then
|
|
TFile.Delete( llFileLocal );
|
|
if TFile.Exists( objFileLocal ) then
|
|
TFile.Delete( objFileLocal );
|
|
if not successLocal and TFile.Exists( dllFileLocal ) then
|
|
TFile.Delete( dllFileLocal );
|
|
end;
|
|
|
|
TThread.Queue( nil,
|
|
procedure
|
|
begin
|
|
Memo.Lines.Add( '---------------------------------------------------------' );
|
|
Memo.Lines.Add( 'DLL wird aufgerufen:' );
|
|
|
|
if TFile.Exists( dllFileLocal ) then
|
|
begin
|
|
var
|
|
Runner := TLLVMRunner.Create( dllFileLocal );
|
|
try
|
|
Runner.Execute( InputInteger ); // <--- HIER DEN INPUT-PARAMETER ÜBERGEBEN
|
|
finally
|
|
Runner.Free;
|
|
end;
|
|
end;
|
|
|
|
Memo.Lines.Add( 'DLL beendet.' );
|
|
Memo.CaretPosition := TCaretPosition.Create( Memo.Lines.Count-1, 1 );
|
|
end );
|
|
end );
|
|
end;
|
|
|
|
procedure TMainForm.WebBrowser1DidFinishLoad( ASender: TObject );
|
|
begin
|
|
if Assigned( FWebMessageReceiver ) then
|
|
Exit;
|
|
|
|
if Supports( WebBrowser1, ICoreWebView2, FWebView ) then
|
|
begin
|
|
FWebMessageReceiver := TWebMessageReceiver.Create( Self );
|
|
FWebView.add_WebMessageReceived( FWebMessageReceiver, FWebMessageReceivedToken );
|
|
end;
|
|
end;
|
|
|
|
end.
|