Search the Community
Showing results for tags 'variadic-arguments'.
Found 1 result
-
I want to implement a variadic method in Delphi that behaves like Write and Writeln, where I can pass a variable number of arguments directly, without using brackets ([]) around the arguments. For example, instead of calling a method like this: MyWriteln(['Hello', 123, 45.67]); // Using array of const with brackets I want to call it like this: MyWriteln('Hello', 123, 45.67); // Without brackets, similar to Writeln I found that Delphi supports varargs for external DLL functions declared with cdecl, like this: procedure Test(aArgs: array of const); varargs; cdecl; external 'externalLibrary.dll'; However, since this approach is limited to external functions, I’d like to know if there’s any way to implement a similar variadic method directly in Delphi, avoiding the need for brackets around the arguments. My main questions are: How can I implement a Delphi variadic method that allows calling it without brackets, similar to Write/Writeln? If it's only possible using varargs with external DLLs, how would I correctly declare and call such a method? Any help or examples would be greatly appreciated! ----- I’m working on a Delphi console application where I want to redirect the standard Write and Writeln output to a synchronized memo viewer in a separate VCL application. Here's a simplified version of my current setup: unit Console.Output.MemoViewer; interface uses Winapi.Windows, System.SysUtils, System.Classes, System.SyncObjs, System.RTTI, System.Generics.Collections; type TOutputViewer = class private fVCLAppPath: string; fStartupInfo: TStartupInfo; fProcessInfo: TProcessInformation; fLogFileName: string; fLogFileStream: TFileStream; fStreamWriter: TStreamWriter; fFileLock: TCriticalSection; procedure SetFileHiddenAttribute(const aFilePath: string); procedure ExtractVCLApp(const aResourceName: string; const aOutputPath: string); procedure LaunchVCLApp; procedure WaitForVCLAppClose; procedure WriteOutput(const aText: string); public constructor Create; destructor Destroy; override; procedure mWriteln(const aArgs: array of const); procedure HandleConsoleClose; stdcall; end; implementation { TOutputViewer } constructor TOutputViewer.Create; begin fVCLAppPath := ExtractFilePath(ParamStr(0)) + 'MemoViewer.exe'; fLogFileName := 'ConsoleOutput.log'; fFileLock := TCriticalSection.Create; // Extract and launch the VCL app ExtractVCLApp('MEMOVIEWER', fVCLAppPath); LaunchVCLApp; // Initialize the log file for writing output fLogFileStream := TFileStream.Create(fLogFileName, fmCreate or fmShareDenyNone); fStreamWriter := TStreamWriter.Create(fLogFileStream, TEncoding.UTF8); end; destructor TOutputViewer.Destroy; begin fFileLock.Acquire; try FreeAndNil(fStreamWriter); FreeAndNil(fLogFileStream); finally fFileLock.Release; FreeAndNil(fFileLock); end; // Wait for VCL app to close and clean up WaitForVCLAppClose; inherited; end; procedure TOutputViewer.SetFileHiddenAttribute(const aFilePath: string); var LFileAttr: DWORD; begin LFileAttr := GetFileAttributes(PChar(aFilePath)); if LFileAttr <> INVALID_FILE_ATTRIBUTES then SetFileAttributes(PChar(aFilePath), LFileAttr or FILE_ATTRIBUTE_HIDDEN); end; procedure TOutputViewer.ExtractVCLApp(const aResourceName: string; const aOutputPath: string); var LResourceStream: TResourceStream; LFileStream: TFileStream; begin if not FileExists(aOutputPath) then begin LResourceStream := TResourceStream.Create(HInstance, aResourceName, RT_RCDATA); try LFileStream := TFileStream.Create(aOutputPath, fmCreate); try LFileStream.CopyFrom(LResourceStream, LResourceStream.Size); finally LFileStream.Free; end; finally LResourceStream.Free; end; SetFileHiddenAttribute(aOutputPath); end; end; procedure TOutputViewer.LaunchVCLApp; begin ZeroMemory(@fStartupInfo, SizeOf(fStartupInfo)); fStartupInfo.cb := SizeOf(fStartupInfo); if not CreateProcess(nil, PChar(fVCLAppPath), nil, nil, False, 0, nil, nil, fStartupInfo, fProcessInfo) then RaiseLastOSError; end; procedure TOutputViewer.WaitForVCLAppClose; begin WaitForSingleObject(fProcessInfo.hProcess, INFINITE); CloseHandle(fProcessInfo.hProcess); CloseHandle(fProcessInfo.hThread); if FileExists(fVCLAppPath) then DeleteFile(PChar(fVCLAppPath)); end; procedure TOutputViewer.WriteOutput(const aText: string); begin fFileLock.Acquire; try fStreamWriter.WriteLine(aText); fStreamWriter.Flush; finally fFileLock.Release; end; end; function VarRecToStr(const V: TVarRec): string; begin case V.VType of vtInteger: Result := IntToStr(V.VInteger); vtBoolean: Result := BoolToStr(V.VBoolean, True); vtChar: Result := V.VChar; vtString: Result := string(V.VString^); vtAnsiString: Result := string(AnsiString(V.VAnsiString)); vtWideString: Result := WideString(V.VWideString); vtUnicodeString: Result := string(UnicodeString(V.VUnicodeString)); vtInt64: Result := IntToStr(V.VInt64^); else Result := '[Unknown]'; end; end; procedure TOutputViewer.mWriteln(const aArgs: array of const); var LText: string; LArg: TVarRec; begin LText := ''; for LArg in aArgs do LText := LText + Format('%s', [VarRecToStr(LArg)]) + ' '; WriteOutput(LText.Trim); writeln(LText.Trim); end; procedure TOutputViewer.HandleConsoleClose; stdcall; begin TerminateProcess(fProcessInfo.hProcess, 0); WaitForVCLAppClose; Halt(0); end; end. program ConsoleMemoViewer; {$APPTYPE CONSOLE} uses SysUtils, Console.Output.MemoViewer in 'API\Console.Output.MemoViewer.pas'; var Viewer: TOutputViewer; begin try Viewer := TOutputViewer.Create; with Viewer do try mWriteln('Hello from redirected Writeln!:', 22, 35.7); mWriteln('This line uses redirected Write:', 22, ', Test 2: ', 35.7); mWriteln('Foo String: ', 22, ', Test 2: ', 35.7, 'Foo string:', 3333); finally Viewer.Free; end; except on E: Exception do Writeln('Error: ', E.Message); end; end. Currently, I'm using an array of const approach for mWriteln, but that requires enclosing the arguments in brackets, like this: mWriteln(['Hello', 123, 45.67]); // Requires brackets I’m aware of the overload solution where multiple versions of mWriteln can be created for different argument counts, but this is not practical when dealing with a large or unknown number of arguments.