Archive
Check out RemObjects Remoting SDK
RemObjects Remoting SDK is one of those component packages that have become more than the sum of it’s part. Just like project Jedi has become standard equipment almost, Remoting SDK is a system that all Delphi and Freepascal developers should have in their toolbox.
In this article I’m going to present the SDK in broad strokes; from a viewpoint of someone who haven’t used the SDK before. There are still a large number of Delphi developers that don’t know it even exists – hopefully this post will shed some light on why the system is worth every penny and what it can do for you.
I should also add, that this is a personal blog. This is not an official RemObjects presentation, but a piece written by me based on my subjective experience and notions. We have a lot of running dialog at Delphi Developer on Facebook, so if I read overly harsh on a subject, that is my personal view as a Delphi Developer.
Stop re-inventing the wheel
Delphi has always been a great tool for writing system services. It has accumulated a vast ecosystem of non-visual components over the years, both commercial and non-commercial, and this allows developers to quickly aggregate and expose complex behavior — everything from graphics processing to databases, file processing to networking.
The challenge for Delphi is that writing large composite systems, where you have more than a single service doing work in concert, is not factored into the RTL or project type. Delphi provides a bare-bone project type for system services, and that’s it. Depending on how you look at it, it’s either a blessing or a curse. You essentially start on C level.
So fundamental things like IPC (inter process communication) is something you have to deal with yourself. If you want multi-tenancy that is likewise not supported out of the box. And all of this is before we venture into protocol standards, message formats and async vs synchronous execution.
The idea behind Remoting SDK is to get away from this style of low-level hacking. Without sounding negative, it provides the missing pieces that Delphi lacks, including the stuff that C# developers enjoy under .net (and then some). So if you are a Delphi developer who look over at C# with smudge of envy, then you are going to love Remoting SDK.
Say goodbye to boilerplate mistakes
Writing distributed servers and services is boring work. For each function you expose, you have to define the parameters and data-types in a portable way, then you have to implement the code that represents the exposed function and finally the interface itself that can be consumed by clients. The latter must be defined in a way that works with other languages too, not just Delphi. So while server tech in it’s essential form is quite simple, it’s the infrastructure that sets the stage of how quickly you can apply improvements and adapt to change.
For example, let’s say you have implemented a wonderful new service. It exposes 60 awesome functions that your customers can consume in their own work. The amount of boilerplate code for 60 distributed functions, especially if you operate with composite data types, is horrendous. It is a nightmare to manage and opens up for sloppy, unnecessary mistakes.
This is where Remoting SDK truly shines. When you install the software, it integrates it’s editors and wizards closely with the Delphi IDE. It adds a ton of new project types, components and whatnot – but the most important feature is without a doubt the service designer.

Start the service-designer in any server or service project and you can edit the methods, data types and interfaces your system expose to the world
As the name implies, the service designer allows you to visually define your services. Adding a new function is a simple click, the same goes for datatypes and structures (record types). These datatypes are exposed too and can be consumed from any modern language. So a service you make in Delphi can be used from C#, C/C++, Java, Oxygene, Swift (and visa-versa).
Auto generated code
A service designer is all good and well I hear you say, but what about that boilerplate code? Well Remoting SDK takes care of that too (kinda the point). Whenever you edit your services, the designer will auto-generate a new interface unit for you. This contains the classes and definitions that describe your service. It will also generate an implementation unit, with empty functions; you just need to fill in the blanks.
The designer is also smart enough not to remove code. So if you go in and change something, it won’t just delete the older implementation procedure. Only the params and names will be changed if you have already written some code.

Having changed a service, hitting F9 re-generates the interface code automatically. Your only job is to fill in the code for each method in the implementation units. The SDK takes care of everything else for you
The service information, including the type information, is stored in a special file format called “rodl”. This format is very close to Microsoft WSDL format, but it holds more information. It’s important to underline that you can import the service directly from your servers (optional naturally) as WSDL. So if you want to consume a Remoting SDK service using Delphi’s ordinary RIO components, that is not a problem. Visual Studio likewise imports and consumes services – so Remoting SDK behaves identical regardless of platform or language used.
Remoting SDK is not just for Delphi, just to be clear on that. If you are presently using both Delphi and C# (which is a common situation), you can buy a license for both C# and Delphi and use whatever language you feel is best for a particular task or service. You can even get Remoting SDK for Javascript and call your service-stack directly from your website if you like. So there are a lot of options for leveraging the technology.
Transport is not content
OK so Remoting SDK makes it easy to define distributed services and servers. But what about communication? Are we boxed into RemObjects way of doing things?
The remoting framework comes with a ton of components, divided into 3 primary groups:
- Servers
- Channels (clients)
- Messages
The reason for this distinction is simple: the ability to transport data, is never the same as the ability to describe data. For example, a message is always connected to a standard. It’s job is ultimately to serialize (represent) and de-serialize data according to a format. The server’s job is to receive a request and send a response. So these concepts are neatly decoupled for maximum agility.
As of writing the SDK offers the following message formats:
- Binary
- Post
- SOAP
- JSON
If you are exposing a service that will be consumed from JavaScript, throwing in a TROJSONMessage component is the way to go. If you expect messages to be posted from your website using ordinary web forms, then TROPostMessage is a perfect match. If you want XML then TROSOAPMessage rocks, and if you want fast, binary messages – well then there is TROBinaryMessage.
What you must understand is that you don’t have to pick just one! You can drop all 4 of these message formats and hook them up to your server or channel. The SDK is smart enough to recognize the format and use the correct component for serialization. So creating a distributed service that can be consumed from all major platforms is a matter of dropping components and setting a property.

If you double-click on a server or channel, you can link message components with a simple click. No messy code snippets in sight.
Multi-tenancy out of the box
With the release of Rad-Server as a part of Delphi, people have started to ask what exactly multi-tenancy is and why it matters. I have to be honest and say that yes, it does matter if you are creating a service stack where you want to isolate the logic for each customer in compartments – but the idea that this is somehow new or unique is not the case. Remoting SDK have given users multi-tenancy support for 15+ years, which is also why I haven’t been too enthusiastic with Rad-Server.
Now don’t get me wrong, I don’t have an axe to grind with Rad-Server. The only reason I mention it is because people have asked how i feel about it. The tech itself is absolutely welcome, but it’s the licensing and throwing Interbase in there that rubs me the wrong way. If it could run on SQLite3 and was free with Enterprise I would have felt different about it.

There are various models for multi-tenancy, but they revolve around the same principles
To get back on topic: multi-tenancy means that you can dynamically load services and expose them on demand. You can look at it as a form of plugin functionality. The idea in Rad-Server is that you can isolate a customer’s service in a separate package – and then load the package into your server whenever you need it.
The reason I dislike Rad-Server in this respect, is because they force you to compile with packages. So if you want to write a Rad-Server system, you have to compile your entire project as package-based, and ship a ton of .dpk files with your system. Packages is not wrong or bad per-se, but they open your system up on a fundamental level. There is nothing stopping a customer from rolling his own spoof package and potentially bypass your security.
There is also an issue with un-loading a package, where right now the package remains in memory. This means that hot-swapping packages without killing the server wont work.
Rad-Server is also hardcoded to use Interbase, which suddenly bring in licensing issues that rubs people the wrong way. Considering the price of Delphi in 2019, Rad-Server stands out as a bit of an oddity. And hardcoding a database into it, with the licensing issues that brings -just rendered the whole system mute for me. Why should I pay more to get less? Especially when I have been using multi-tenancy with RemObjects for some 15 years?
With Remoting SDK you have something called DLL servers, which does the exact same thing – but using ordinary DLL files (not packages!). You don’t have to compile your system with packages, and it takes just one line of code to make your main dispatcher aware of the loaded service.
This actually works so well that I use Remoting SDK as my primary “plugin” system. Even when I write ordinary desktop applications that has nothing to do with servers or services – I always try to compartmentalize features that could be replaced in the future.
For example, I’m a huge fan of ElevateDB, which is a native Delphi database engine that compiles directly into your executable. By isolating that inside a DLL as a service, my application is now engine agnostic – and I get a break from buying a truck load of components every time Delphi is updated.
Saving money
The thing about DLL services, is that you can save a lot of money. I’m actually using an ElevateDB license that was for Delphi 2007. I compiled the engine using D2007 into a DLL service — and then I consume that DLL from my more modern Delphi editions. I have no problem supporting or paying for components, that is right and fair, but having to buy new licenses for every single component each time Delphi is updated? This is unheard of in other languages, and I would rather ditch the platform all together than forking out $10k ever time I update.

A DLL server can be used for many things if you are creative about it
While we are on the subject – Hydra is another great money saver. It allows you to use .net and Java libraries (both visual and non-visual) with Delphi. With Hydra you can design something in .net, compile it into a DLL file, and then use that from Delphi.
But — you can also compile things from Delphi, and use it in newer versions of Delphi. Im not forking out for a Developer Express update just to use what I have already paid for in the latest Delphi. I have one license, I compile the forms and components into a Hydra Module — and then use it from newer Delphi editions.

Hydra, which is a separate product, allows you to stuff visual components and forms inside a vanilla DLL. It allows cross language use, so you can finally use Java and .net components inside your Delphi application
Bonjour support
Another feature I love is the zero configuration support. This is one of those things that you often forget, but that suddenly becomes important once you deploy a service stack on cluster level.
Remoting SDK comes with support for Apple Bonjour, so if you want to use that functionality you have to install the Bonjour library from Apple. Once installed on your host machines, your RemObjects services can find each other.
ZeroConfig is not that hard to code manually. You can roll your own using UDP or vanilla messages. But getting service discovery right can be fiddly. One thing is broadcasting an UDP message saying “here I am”, it’s something else entirely to allow service discovery on cluster level.
If Bonjour is not your cup of tea, the SDK provides a second option, which is RemObjects own zero-config hub. You can dig into the documentation to find out more about this.
What about that IPC stuff you mentioned?
I mentioned IPC (inter process communication) at the beginning here, which is a must have if you are making a service stack where each member is expected to talk to the others. In a large server-system the services might not exist on the same, physical hardware either, so you want to take height for that.
With the SDK this is just another service. It takes 10 minutes to create a DLL server with the functionality to send and receive messages – and then you just load and plug that into all your services. Done. Finished.
Interestingly, Remoting SDK supports named-pipes. So if you are running on a Windows network it’s even easier. Personally I prefer to use a vanilla TCP/IP based server and channel, that way I can make use of my Linux blades too.
Building on the system
There is nothing stopping you from expanding the system that RemObjects have established. You are not forced to only use their server types, message types and class framework. You can mix and match as you see fit – and also inherit out your own variation if you need something special.
For example, WebSocket is an emerging standard that has become wildly popular. Remoting SDK does not support that out of the box, the reason is that the standard is practically identical to the RemObjects super-server, and partly because there must be room for third party vendors.
Andre Mussche took the time to implement a WebSocket server for Remoting SDK a few years back. Demonstrating in the process just how easy it is to build on the existing infrastructure. If you are already using Remoting SDK or want WebSocket support, head over to his github repository and grab the code there: https://github.com/andremussche/DelphiWebsockets
I could probably write a whole book covering this framework. For the past 15 years, RemObjects Remoting SDK is the first product I install after Delphi. It has become standard for me and remains an integral part of my toolkit. Other packages have come and gone, but this one remains.
Hopefully this post has tickled your interest in the product. No matter if you are maintaining a legacy service stack, or thinking about re implementing your existing system in something future-proof, this framework will make your life much, much easier. And it wont break the bank either.
You can visit the product page here: https://www.remotingsdk.com/ro/default.aspx
And you can check out the documentation here: https://docs.remotingsdk.com/
30% discount on all RemObjects products!
This is brilliant. RemObjects is giving a whopping 30% discount on all products!
This means you can now pick up RemObjects Remoting Framework, Data Abstract, Hydra or the Elements compiler toolchain – with a massive 30% saving!
These are battle-hardened, enterprise level solutions that have been polished over years and they are in constant development. Each solution integrates seamlessly into Embarcadero Delphi and provides a smooth path to delivering quality products in days rather than weeks.
But you better hurry because it’s only valid for one week (!)
Use the coupon code: “DelphiDeveloper”
A Delphi propertybag
A long, long time ago, way back in the previous century, I often had to adjust a Visual Basic project my company maintained. Going from object-pascal to VB was more than a little debilitating; Visual Basic was not a compiled language like Delphi is, and it lacked more or less every feature you needed to produce good software.

I could probably make a VB clone using Delphi pretty easily. But I think the world has experienced enough suffering, no need to add more evil to the universe
Having said that, I have always been a huge fan of Basic (it was my first language after all, it’s what schools taught in the 70s and 80s). I think it was a terrible mistake for Microsoft to retire Basic as a language, because it’s a great way to teach kids the fundamentals of programming.
Visual Basic is still there though, available for the .Net framework, but to call it Basic is an insult of the likes of GFA Basic, Amos Basic and Blitz Basic; the mighty compilers of the past. If you enjoyed basic before Microsoft pushed out the monstrosity that is Visual Basic, then perhaps swing by GitHub and pick up a copy of BlitzBasic? BlitzBasic is a completely different beast. It compiles to machine-code, allows inline assembly, and has been wildly popular for game developers over the years.
A property bag
The only feature that I found somewhat useful in Visual Basic, was an object called a propertybag. It’s just a fancy name for a dictionary, but it had a couple of redeeming factors beyond lookup ability. Like being able to load name-value-pairs from a string, recognizing datatypes and exposing type-aware read/write methods. Nothing fancy but handy when dealing with database connection-strings, shell parameters and the like.
So you could feed it strings like this:
first=12;second=hello there;third=3.14
And the class would parse out the names and values, stuff it in a dictionary, and you could easily extract the data you needed. Nothing fancy, but handy on rare occasions.
A Delphi version
Im mostly porting code from Delphi to Oxygene these days, but here is my Delphi implementation of the propertybag object. Please note that I haven’t bothered to implement the propertybag available in .Net. The Delphi version below is based on the Visual Basic 6 version, with some dependency injection thrown in for good measure.
unit fslib.params; interface {.$DEFINE SUPPORT_URI_ENCODING} uses System.SysUtils, System.Classes, Generics.Collections; type (* Exceptions *) EPropertybag = class(exception); EPropertybagReadError = class(EPropertybag); EPropertybagWriteError = class(EPropertybag); EPropertybagParseError = class(EPropertybag); (* Datatypes *) TPropertyBagDictionary = TDictionary ; IPropertyElement = interface ['{C6C937DF-50FA-4984-BA6F-EBB0B367D3F3}'] function GetAsInt: integer; procedure SetAsInt(const Value: integer); function GetAsString: string; procedure SetAsString(const Value: string); function GetAsBool: boolean; procedure SetAsBool(const Value: boolean); function GetAsFloat: double; procedure SetAsFloat(const Value: double); function GetEmpty: boolean; property Empty: boolean read GetEmpty; property AsFloat: double read GetAsFloat write SetAsFloat; property AsBoolean: boolean read GetAsBool write SetAsBool; property AsInteger: integer read GetAsInt write SetAsInt; property AsString: string read GetAsString write SetAsString; end; TPropertyBag = Class(TInterfacedObject) strict private FLUT: TPropertyBagDictionary; strict protected procedure Parse(NameValuePairs: string); public function Read(Name: string): IPropertyElement; function Write(Name: string; Value: string): IPropertyElement; procedure SaveToStream(const Stream: TStream); procedure LoadFromStream(const Stream: TStream); function ToString: string; override; procedure Clear; virtual; constructor Create(NameValuePairs: string); virtual; destructor Destroy; override; end; implementation {$IFDEF SUPPORT_URI_ENCODING} uses system.NetEncoding; {$ENDIF} const cnt_err_sourceparameters_parse = 'Failed to parse input, invalid or damaged text error [%s]'; cnt_err_sourceparameters_write_id = 'Write failed, invalid or empty identifier error'; cnt_err_sourceparameters_read_id = 'Read failed, invalid or empty identifier error'; type TPropertyElement = class(TInterfacedObject, IPropertyElement) strict private FName: string; FData: string; FStorage: TPropertyBagDictionary; strict protected function GetEmpty: boolean; inline; function GetAsInt: integer; inline; procedure SetAsInt(const Value: integer); inline; function GetAsString: string; inline; procedure SetAsString(const Value: string); inline; function GetAsBool: boolean; inline; procedure SetAsBool(const Value: boolean); inline; function GetAsFloat: double; inline; procedure SetAsFloat(const Value: double); inline; public property AsFloat: double read GetAsFloat write SetAsFloat; property AsBoolean: boolean read GetAsBool write SetAsBool; property AsInteger: integer read GetAsInt write SetAsInt; property AsString: string read GetAsString write SetAsString; property Empty: boolean read GetEmpty; constructor Create(const Storage: TPropertyBagDictionary; Name: string; Data: string); overload; virtual; constructor Create(Data: string); overload; virtual; end; //############################################################################# // TPropertyElement //############################################################################# constructor TPropertyElement.Create(Data: string); begin inherited Create; FData := Data.Trim(); end; constructor TPropertyElement.Create(const Storage: TPropertyBagDictionary; Name: string; Data: string); begin inherited Create; FStorage := Storage; FName := Name.Trim().ToLower(); FData := Data.Trim(); end; function TPropertyElement.GetEmpty: boolean; begin result := FData.Length < 1; end; function TPropertyElement.GetAsString: string; begin result := FData; end; procedure TPropertyElement.SetAsString(const Value: string); begin if Value FData then begin FData := Value; if FName.Length > 0 then begin if FStorage nil then FStorage.AddOrSetValue(FName, Value); end; end; end; function TPropertyElement.GetAsBool: boolean; begin TryStrToBool(FData, result); end; procedure TPropertyElement.SetAsBool(const Value: boolean); begin FData := BoolToStr(Value, true); if FName.Length > 0 then begin if FStorage nil then FStorage.AddOrSetValue(FName, FData); end; end; function TPropertyElement.GetAsFloat: double; begin TryStrToFloat(FData, result); end; procedure TPropertyElement.SetAsFloat(const Value: double); begin FData := FloatToStr(Value); if FName.Length > 0 then begin if FStorage nil then FStorage.AddOrSetValue(FName, FData); end; end; function TPropertyElement.GetAsInt: integer; begin TryStrToInt(FData, Result); end; procedure TPropertyElement.SetAsInt(const Value: integer); begin FData := IntToStr(Value); if FName.Length > 0 then begin if FStorage nil then FStorage.AddOrSetValue(FName, FData); end; end; //############################################################################# // TPropertyBag //############################################################################# constructor TPropertyBag.Create(NameValuePairs: string); begin inherited Create; FLUT := TDictionary.Create(); NameValuePairs := NameValuePairs.Trim(); if NameValuePairs.Length > 0 then Parse(NameValuePairs); end; destructor TPropertyBag.Destroy; begin FLut.Free; inherited; end; procedure TPropertyBag.Clear; begin FLut.Clear; end; procedure TPropertyBag.Parse(NameValuePairs: string); var LList: TStringList; x: integer; LId: string; LValue: string; LOriginal: string; {$IFDEF SUPPORT_URI_ENCODING} LPos: integer; {$ENDIF} begin // Reset content FLUT.Clear(); // Make a copy of the original text LOriginal := NameValuePairs; // Trim and prepare NameValuePairs := NameValuePairs.Trim(); // Anything to work with? if NameValuePairs.Length > 0 then begin {$IFDEF SUPPORT_URI_ENCODING} // Check if the data is URL-encoded LPos := pos('%', NameValuePairs); if (LPos >= low(NameValuePairs) ) and (LPos 0 then Begin (* Populate our lookup table *) LList := TStringList.Create; try LList.Delimiter := ';'; LList.StrictDelimiter := true; LList.DelimitedText := NameValuePairs; if LList.Count = 0 then raise EPropertybagParseError.CreateFmt(cnt_err_sourceparameters_parse, [LOriginal]); try for x := 0 to LList.Count-1 do begin LId := LList.Names[x].Trim().ToLower(); if (LId.Length > 0) then begin LValue := LList.ValueFromIndex[x].Trim(); Write(LId, LValue); end; end; except on e: exception do raise EPropertybagParseError.CreateFmt(cnt_err_sourceparameters_parse, [LOriginal]); end; finally LList.Free; end; end; end; end; function TPropertyBag.ToString: string; var LItem: TPair; begin setlength(result, 0); for LItem in FLut do begin if LItem.Key.Trim().Length > 0 then begin result := result + Format('%s=%s;', [LItem.Key, LItem.Value]); end; end; end; procedure TPropertyBag.SaveToStream(const Stream: TStream); var LData: TStringStream; begin LData := TStringStream.Create(ToString(), TEncoding.UTF8); try LData.SaveToStream(Stream); finally LData.Free; end; end; procedure TPropertyBag.LoadFromStream(const Stream: TStream); var LData: TStringStream; begin LData := TStringStream.Create('', TEncoding.UTF8); try LData.LoadFromStream(Stream); Parse(LData.DataString); finally LData.Free; end; end; function TPropertyBag.Write(Name: string; Value: string): IPropertyElement; begin Name := Name.Trim().ToLower(); if Name.Length > 0 then begin if not FLUT.ContainsKey(Name) then FLut.Add(Name, Value); result := TPropertyElement.Create(FLut, Name, Value) as IPropertyElement; end else raise EPropertybagWriteError.Create(cnt_err_sourceparameters_write_id); end; function TPropertyBag.Read(Name: string): IPropertyElement; var LData: String; begin Name := Name.Trim().ToLower(); if Name.Length > 0 then begin if FLut.TryGetValue(Name, LData) then result := TPropertyElement.Create(LData) as IPropertyElement else raise EPropertybagReadError.Create(cnt_err_sourceparameters_read_id); end else raise EPropertybagReadError.Create(cnt_err_sourceparameters_read_id); end; end.
BTree for Delphi
A few weeks back I posted an article on RemObjects blog regarding universal code, and how you with a little bit of care can write code that easily compiled with both Oxygene, Delphi and Freepascal. With emphasis on Oxygene.
The example I used was a BTree class that I originally ported from Delphi to Smart Pascal, and then finally to Oxygene to run under WebAssembly.
Long story short I was asked if I could port the code back to Delphi in its more or less universal form. Naturally there are small differences here and there, but nothing special that distinctly separates the Delphi version from Oxygene or Smart Pascal.
Why this version?
If you google BTree and Delphi you will find loads of implementations. They all operate more or less identical, using records and pointers for optimal speed. I decided to base my version on classes for convenience, but it shouldn’t be difficult to revert that to use records if you absolutely need it.
What I like about this BTree implementation is that it’s very functional. Its easy to traverse the nodes using the ForEach() method, you can add items using a number as an identifier, but it also supports string identifiers.
I also changed the typical data reference. The data each node represent is usually a pointer. I changed this to variant to make it more functional.
Well, here is the Delphi version as promised. Happy to help.
unit btree; interface uses System.Generics.Collections, System.Sysutils, System.Classes; type // BTree leaf object TQTXBTreeNode = class(TObject) public Identifier: integer; Data: variant; Left: TQTXBTreeNode; Right: TQTXBTreeNode; end; [Weak] TQTXBTreeProcessCB = reference to procedure (const Node: TQTXBTreeNode; var Cancel: boolean); EBTreeError = class(Exception); TQTXBTree = class(TObject) private FRoot: TQTXBTreeNode; FCurrent: TQTXBTreeNode; protected function GetEmpty: boolean; virtual; function GetPackedNodes: TList; public property Root: TQTXBTreeNode read FRoot; property Empty: boolean read GetEmpty; function Add(const Ident: integer; const Data: variant): TQTXBTreeNode; overload; virtual; function Add(const Ident: string; const Data: variant): TQTXBTreeNode; overload; virtual; function Contains(const Ident: integer): boolean; overload; virtual; function Contains(const Ident: string): boolean; overload; virtual; function Remove(const Ident: integer): boolean; overload; virtual; function Remove(const Ident: string): boolean; overload; virtual; function Read(const Ident: integer): variant; overload; virtual; function Read(const Ident: string): variant; overload; virtual; procedure Write(const Ident: string; const NewData: variant); overload; virtual; procedure Write(const Ident: integer; const NewData: variant); overload; virtual; procedure Clear; overload; virtual; procedure Clear(const Process: TQTXBTreeProcessCB); overload; virtual; function ToDataArray: TList; function Count: integer; procedure ForEach(const Process: TQTXBTreeProcessCB); destructor Destroy; override; end; implementation //############################################################################# // TQTXBTree //############################################################################# destructor TQTXBTree.Destroy; begin if FRoot nil then Clear(); inherited; end; procedure TQTXBTree.Clear; var lTemp: TList; x: integer; begin if FRoot nil then begin // pack all nodes to a linear list lTemp := GetPackedNodes(); try // release each node for x := 0 to ltemp.Count-1 do begin lTemp[x].Free; end; finally // dispose of list lTemp.Free; // reset pointers FCurrent := nil; FRoot := nil; end; end; end; procedure TQTXBTree.Clear(const Process: TQTXBTreeProcessCB); begin ForEach(Process); Clear(); end; function TQTXBTree.GetPackedNodes: TList; var LData: Tlist; begin LData := TList.Create(); ForEach( procedure (const Node: TQTXBTreeNode; var Cancel: boolean) begin LData.Add(Node); Cancel := false; end); result := LData; end; function TQTXBTree.GetEmpty: boolean; begin result := FRoot = nil; end; function TQTXBTree.Count: integer; var LCount: integer; begin ForEach( procedure (const Node: TQTXBTreeNode; var Cancel: boolean) begin inc(LCount); Cancel := false; end); result := LCount; end; function TQTXBTree.ToDataArray: TList; var Data: TList; begin Data := TList.Create(); ForEach( procedure (const Node: TQTXBTreeNode; var Cancel: boolean) begin Data.add(Node.data); Cancel := false; end); result := data; end; function TQTXBTree.Add(const Ident: string; const Data: variant): TQTXBTreeNode; begin result := Add( Ident.GetHashCode(), Data); end; function TQTXBTree.Add(const Ident: integer; const Data: variant): TQTXBTreeNode; var lNode: TQTXBtreeNode; begin LNode := TQTXBTreeNode.Create(); LNode.Identifier := Ident; LNode.Data := data; if FRoot = nil then FRoot := LNode; FCurrent := FRoot; while true do begin if (Ident FCurrent.Identifier) then begin if (FCurrent.right = nil) then begin FCurrent.right := LNode; break; end else FCurrent := FCurrent.right; end else break; end; result := LNode; end; function TQTXBTree.Read(const Ident: string): variant; begin result := Read( Ident.GetHashCode() ); end; function TQTXBTree.Read(const Ident: integer): variant; begin FCurrent := FRoot; while FCurrent nil do begin if (Ident Fcurrent.Identifier) then FCurrent := FCurrent.Right else begin result := FCUrrent.Data; break; end end; end; procedure TQTXBTree.Write(const Ident: string; const NewData: variant); begin Write( Ident.GetHashCode(), NewData); end; procedure TQTXBTree.Write(const Ident: integer; const NewData: variant); begin FCurrent := FRoot; while (FCurrent nil) do begin if (Ident Fcurrent.Identifier) then FCurrent := FCurrent.Right else begin FCurrent.Data := NewData; break; end end; end; function TQTXBTree.Contains(const Ident: string): boolean; begin result := Contains( Ident.GetHashCode() ); end; function TQTXBTree.Contains(const Ident: integer): boolean; begin result := false; if FRoot nil then begin FCurrent := FRoot; while ( (not Result) and (FCurrent nil) ) do begin if (Ident Fcurrent.Identifier) then FCurrent := FCurrent.Right else begin Result := true; break; end end; end; end; function TQTXBTree.Remove(const Ident: string): boolean; begin result := Remove( Ident.GetHashCode() ); end; function TQTXBTree.Remove(const Ident: integer): boolean; var LFound: boolean; LParent: TQTXBTreeNode; LReplacement, LReplacementParent: TQTXBTreeNode; LChildCount: integer; begin FCurrent := FRoot; LFound := false; LParent := nil; LReplacement := nil; LReplacementParent := nil; while (not LFound) and (FCurrent nil) do begin if (Ident FCurrent.Identifier) then begin LParent := FCurrent; FCurrent := FCurrent.right; end else LFound := true; if LFound then begin LChildCount := 0; if (FCurrent.left nil) then inc(LChildCount); if (FCurrent.right nil) then inc(LChildCount); if FCurrent = FRoot then begin case (LChildCOunt) of 0: begin FRoot := nil; end; 1: begin if FCurrent.right = nil then FRoot := FCurrent.left else FRoot :=FCurrent.Right; end; 2: begin LReplacement := FRoot.left; while (LReplacement.right nil) do begin LReplacementParent := LReplacement; LReplacement := LReplacement.right; end; if (LReplacementParent nil) then begin LReplacementParent.right := LReplacement.Left; LReplacement.right := FRoot.Right; LReplacement.left := FRoot.left; end else LReplacement.right := FRoot.right; end; end; FRoot := LReplacement; end else begin case LChildCount of 0: if (FCurrent.Identifier < LParent.Identifier) then Lparent.left := nil else LParent.right := nil; 1: if (FCurrent.Identifier < LParent.Identifier) then begin if (FCurrent.Left = NIL) then LParent.left := FCurrent.Right else LParent.Left := FCurrent.Left; end else begin if (FCurrent.Left = NIL) then LParent.right := FCurrent.Right else LParent.right := FCurrent.Left; end; 2: begin LReplacement := FCurrent.left; LReplacementParent := FCurrent; while LReplacement.right nil do begin LReplacementParent := LReplacement; LReplacement := LReplacement.right; end; LReplacementParent.right := LReplacement.left; LReplacement.right := FCurrent.right; LReplacement.left := FCurrent.left; if (FCurrent.Identifier < LParent.Identifier) then LParent.left := LReplacement else LParent.right := LReplacement; end; end; end; end; end; result := LFound; end; procedure TQTXBTree.ForEach(const Process: TQTXBTreeProcessCB); function ProcessNode(const Node: TQTXBTreeNode): boolean; begin if Node nil then begin if Node.left nil then begin result := ProcessNode(Node.left); if result then exit; end; Process(Node, result); if result then exit; if (Node.right nil) then begin result := ProcessNode(Node.right); if result then exit; end; end; end; begin ProcessNode(FRoot); end; end.
Calling node.js from Delphi
We got a good question about how to start a node.js program from Delphi on our Facebook group today (third one in a week?). When you have been coding for years you often forget that things like this might not be immediately obvious. Hopefully I can shed some light on the options in this post.
Node or chrome?
Just to be clear: node.js has nothing to do with chrome or chromium embedded. Chrome is a web-browser, a completely visual environment and ecosystem.
Node.js is the complete opposite. It is purely a shell based environment, meaning that it’s designed to run services and servers, with emphasis on the latter.
The only thing node.js and chrome have in common, is that they both use the V8 JavaScript runtime engine to load, JIT compile and execute scripts at high speed. Beyond that, they are utterly alien to each other.
Can node.js be embedded into a Delphi program?
Technically there is nothing stopping a C/C++ developer from compiling the node.js core system as C++ builder compatible .obj files; files that can then be linked into a Delphi application through references. But this also requires a bit of scaffolding, like adding support for malloc_, free_ and a few other procedures – so that your .obj files uses the same memory manager as your Delphi code. But until someone does just that and publish it, im afraid you are stuck with two options:
- Use a library called Toby, that keeps node.js in a single DLL file. This is the most practical way if you insist on hosting your own version of node.js
- Add node.js as a prerequisite and give users the option to locate the node.exe in your application’s preferences. This is the way I would go, because you really don’t want to force users to stick with your potentially outdated or buggy build.
So yes, you can use toby and just add the toby dll file to your program folder, but I have to strongly advice against that. There is no point setting yourself up for maintaining a whole separate programming language, just because you want JavaScript support.
“How many in your company can write high quality WebAssembly modules?”
If all you want to do is support JavaScript in your application, then I would much rather install Besen into Delphi. Besen is a JavaScript runtime engine written in Freepascal. It is fully compatible with Delphi, and follows the ECMA standard to the letter. So it is extremely compatible, fast and easy to use.
Like all Delphi components Besen is compiled into your application, so you have no dependencies to worry about.
Starting a node.js script
The easiest way to start a node.js script, is to simply shell-execute out of your Delphi application. This can be done as easily as:
ShellExecute(Handle, 'open', PChar('node.exe'), pchar('script.js'), nil, SW_SHOW);
This is more than enough if you just want to start a service, server or do some work that doesn’t require that you capture the result.
If you need to capture the result, the data that your node.js program emits on stdout, there is a nice component in the Jedi Component Library. Also plenty of examples online on how to do that.
If you need even further communication, you need to look for a shell-execute that support pipes. All node.js programs have something called a message-channel in the Javascript world. In reality though, this is just a named pipe that is automatically created when your script starts (with the same moniker as the PID [process identifier]).
If you opt for the latter you have a direct, full duplex message channel directly into your node.js application. You just have to agree with yourself on a protocol so that your Delphi code understands what node.js is saying, and visa versa.
UDP or TCP
If you don’t want to get your hands dirty with named pipes and rolling your own protocol, you can just use UDP to let your Delphi application communicate with your node.js process. UDP is practically without cost since its fundamental to all networking stacks, and in your case you will be shipping messages purely between processes on localhost. Meaning: packets are never sent on the network, but rather delegated between processes on the same machine.
In that case, I suggest you ship in the port you want your UDP server to listen on, so that your node.js service acts as the server. A simple command-line statement like:
node.exe myservice.js 8090
Inside node.js you can setup an UDP server with very little fuzz:
function setupServer(port) { var os = require("os"); var dgram = require("dgram"); var socket = dgram.createSocket("udp4"); var MULTICAST_HOST = "224.0.0.236"; var BROADCAST_HOST = "255.255.255.255"; var ALL_PORT = 60540; var MULTICAST_TTL = 1; // Local network socket.bind(port); socket.on('listening', function() { socket.setMulticastLoopback(true); socket.setMulticastTTL(MULTICAST_TTL); socket.addMembership(multicastHost); if(broadcast) { socket.setBroadcast(true); } }); socket.on('message', parseMessage); } function parseMessage(message, rinfo) { try { var messageObject = JSON.parse(message); var eventType = messageObject.eventType; } catch(e) { } }
Note: the code above assumes a JSON text message.
You can then use any Delphi UDP client to communicate with your node.js server, Indy is good, Synapse is a good library with less overhead – there are many options here.
Do I have to learn Javascript to use node.js?
If you download DWScript you can hook-up the JS-codegen library (see library folder in the DWScript repository), and use that to compile DWScript (object pascal) to kick-ass Javascript. This is the same compiler that was used in Smart Mobile Studio.
“Adding WebAssembly to your resume is going to be a hell of a lot more valuable in the years to come than C# or Java”
Another alternative is to use Freepascal, they have a pas2js project where you can compile ordinary object-pascal to javascript. Naturally there are a few things to keep in mind, both for DWScript and Freepascal – like avoiding pointers. But clean object pascal compiles just fine.
If JavaScript is not your cup of tea, or you simply don’t have time to learn the delicate nuances between the DOM (document object model, used by browsers) and the 100% package oriented approach deployed by node.js — then you can just straight up to webassembly.
RemObjects Software has a kick-ass webassembly compiler, perfect if you dont have the energy or time to learn JavaScript. As of writing this is the fastest and most powerful toolchain available. And I have tested them all.
WebAssembly, no Javascript needed
You might remember Oxygene? It used to be shipped with Delphi as a way to target Microsoft CLR (common language runtime) and the .net framework.
Since then Oxygene and the RemObjects toolchain has evolved dramatically and is now capable of a lot more than CLR support.
- You can compile to raw, llvm optimized machine code for 8 platforms
- You can compile to CLR/.Net
- You can compile to Java bytecodes
- You can compile to WebAssembly!
WebAssembly is not Javascript, it’s important to underline that. WebAssembly was created especially for developers using traditional languages, so that traditional compilers can emit web friendly, binary code. Unlike Javascript, WebAssembly is a purely binary format. Just like Delphi generates machine-code that is linked into a final executable, WebAssembly is likewise compiled, linked and emitted in binary form.
If that sounds like a sales pitch, it’s not. It’s a matter of practicality.
- WebAssembly is completely barren out of the box. The runtime environment, be it V8 for the browser or V8 for node.js, gives you nothing out of the box. You don’t even have WriteLn() to emit text.
- Google expects compiler makers to provide their own RTL functions, from the fundamental to the advanced. The only thing V8 gives you, is a barebone way of referencing objects and functions on the other side, meaning the JS and DOM world. And that’s it.
So the reason i’m talking a lot about Oxygene and RemObjects Elements (Elements is the name of the compiler toolchain RemObjects offers), is because it ships with an RTL. So you are not forced to start on actual, literal assembly level.
RemObjects also delivers a DelphiVCL compatibility framework. This is a clone of the Delphi VCL / Freepascal LCL. Since WebAssembly is still brand new, work is being done on this framework on a daily basis, with updates being issued all the time.
Note: The Delphi VCL framework is not just for WebAssembly. It represents a unified framework that can work anywhere. So if you switch from WebAssembly to say Android, you get the same result.
The most important part of the above, is actually not the visual stuff. I mean, having HTML5 visual controls is cool – but chances are you want to use a library like Sencha, SwiftUI or jQueryUI to compose your forms right? Which means you just want to interface with the widgets in the DOM to set and get values.

You probably want to use a fancy UI library, like jQuery UI. This works perfectly with Elements because you can reference the controls from your WebAssembly module. You dont have to create TButton, TListbox etc manually
The more interesting stuff is actually the non-visual code you get access to. Hundreds of familiar classes from the VCL, painstakingly re-created, and usable from any of the 5 languages Elements supports.
You can check it out here: https://github.com/remobjects/DelphiRTL
Skipping JavaScript all together
I dont believe in single languages. Not any more. There was a time when all you needed was Delphi and a diploma and you were set to conquer the world. But those days are long gone, and a programmer needs to be flexible and have a well stocked toolbox.

Knowing where you want to be is half the journey
The world really don’t need yet-another-c# developer. There are millions of C# developers in India alone. C# is just “so what?”. Which is also why C# jobs pays less than Delphi or node.js system service jobs.
What you want, is to learn the things others avoid. If JavaScript looks alien and you feel uneasy about the whole thing – that means you are growing as a developer. All new things are learned by venturing outside your comfort zone.
How many in your company can write high quality WebAssembly modules?
How many within one hour driving distance from your office or home are experts at WebAssembly? How many are capable of writing industrial scale, production ready system services for node.js that can scale from a single instance to 1000 instances in a large, clustered cloud environment?
Any idiot can pick up node.js and knock out a service, but with your background from Delphi or C++ builder you have a massive advantage. All those places that can throw an exception that JS devs usually ignore? As a Delphi or Oxygene developer you know better. And when you re-apply that experience under a different language, suddenly you can do stuff others cant. Which makes your skills valuable.

The Quartex Media Desktop have made even experienced node / web developers gasp. They are not used to writing custom-controls and large-scale systems, which is my advantage
So would you learn JavaScript or just skip to WebAssembly? Honestly? Learn a bit of both. You don’t have to be an expert in JavaScript to compliment WebAssembly. Just get a cheap book, like “Node.js for beginners” and “JavaScript the good parts” ($20 a piece) and that should be more than enough to cover the JS side of things.
Adding WebAssembly to your resume and having the material to prove you know your stuff, is going to be a hell of a lot more valuable in the years to come than C#, Java or Python. THAT I can guarantee you.
And, we have a wicked cool group on Facebook you can join too: Click here to visit RemObjects Developer.
Enumerating network adapters in DWScript/Smart under Node.js
This is something I never had the time to implement under Smart Pascal, but it should be easy enough to patch. If you are using DWScript with the QTX Framework this is already in place. But for Smart users, here is a quick recipe.
First, we need access to the node.js OS module:
unit qtx.node.os; //############################################################################# // Quartex RTL for DWScript // Written by Jon L. Aasenden, all rights reserved // This code is released under modified LGPL (see license.txt) //############################################################################# unit NodeJS.os; interface uses NodeJS.Core; type TCpusResultObjectTimes = class external property user: Integer; property nice: Integer; property sys: Integer; property idle: Integer; property irq: Integer; end; TCpusResult = class external property model: String; property speed: Integer; property times: TcpusResultObjectTimes; end; JNetworkInterfaceInfo = class external property address: string; property netmask: string; property family: string; property mac: string; property scopeid: integer; property internal: boolean; property cidr: string; end; Jos_Exports = class external public function tmpDir: String; function hostname: String; function &type: String; function platform: String; function arch: String; function release: String; function uptime: Integer; function loadavg: array of Integer; function totalmem: Integer; function freemem: Integer; function cpus: array of TCpusResult; function networkInterfaces: variant; property EOL: String; end; function NodeJSOsAPI: Jos_Exports; implementation function NodeJSOsAPI: Jos_Exports; begin result := Jos_Exports(RequireModule("os") ); end; end.
With that in place, we can start enumerating through the adapters. Remember that a PC can have several adapters attached, from a dedicated card to X number of USB wifi sticks.
Here is a little routine that goes through the adapters, and returns the first IPv4 LAN address it finds. This is very useful when writing servers, since you need the IP + port to setup a binding. And yes, you can just call HostName(), but the point here is to know how to run through the adapter array.
function GetMyV4LanIP: string; begin var OSAPI := NodeJSOsAPI(); var NetAdapters := OSAPI.networkInterfaces(); for var Adapter in NetAdapters do begin // Skip loopback device if Adapter.Contains('Loopback') then continue; for var netIntf in NetAdapters[Adapter] do begin var address = JNetworkInterfaceInfo( NetAdapters[Adapter][netIntf] ); if not address.internal then begin // force copy of string var lFam: string := string(address.family) + " "; // make sure its ipv4 if lFam.ToLower().Trim() = 'ipv4' then begin result := address.address + " "; result := result.trim(); break; end; end; end; end; if result.length < 1 then result := '127.0.0.1'; end;
Getting into Node.js from Delphi
Delphi is one of the best development toolchains for Windows. I have been an avid fan of Delphi since it was first released, and before that – Turbo Pascal too. Delphi has a healthy following – and despite popular belief, Delphi scores quite well on the Tiobe Index.
As cool and efficient as Delphi might be, there are situations where native code wont work. Or at the very least, be less efficient than the alternatives. Delphi has a broad wingspan, from low-level assembler all the way to classes and generics. But JavaScript and emerging web technology is based on a completely different philosophy, one where native code is regarded as negative since it binds you to hardware.
Getting to grips with the whole JavaScript phenomenon, be it for mobile, embedded or back-end services, can be daunting if all you know is native code. But thankfully there are alternatives that can help you become productive quickly, something I will brush over in this post.
JavaScript without JavaScript
Before we dig into the tools of the trade, I want to cover alternative ways of enjoying the power of node.js and Javascript. Namely by using compilers that can convert code from a traditional language – and emit fully working JavaScript. There are a lot more options than you think:

Quartex Media Desktop is a complete environment written purely in JavaScript. Both Server, Cluster and front-end is pure JavaScript. A good example of what can be done.
- Swift compiles for JavaScript, and Apple is doing some amazing things with the new and sexy SwiftUI tookit. If you know your way around Swift, you can compile for Javascript
- Go can likewise be compiled to JS:
- C/C++ can be compiled to asm.js courtesy of EmScripten. It uses clang to first compile your code to llvm bitcode, and then it converts that into asm.js. You have probably seen games like Quake run in the browser? That was asm.js, a kind of precursor to WebAssembly.
- NS Basic compiles for JavaScript, this is a Visual Basic 6 style environment with its own IDE even
For those coming straight from Delphi, there are a couple of options to pick from:
- Freepascal (pas2js project)
- DWScript compiles code to JavaScript, this is the same compiler that we used in Smart Pascal earlier
- Oxygene, the next generation object-pascal from RemObjects compiles to WebAssembly. This is by far the best option of them all.

I strongly urge you to have a look at Elements, here running in Visual Studio
JavaScript, Asm.js or WebAssembly?
Asm.js is by far the most misunderstood technology in the JavaScript ecosystem, so let me just cover that before we move on:
A few years back JavaScript gained support for memory buffers and typed arrays. This might not sound very exciting, but in terms of speed – the difference is tremendous. The default variable type in JavaScript is what Delphi developers know as Variant. It assumes the datatype of the values you assign to it. Needless to say, there is a lot of overhead when working with variants – so JavaScript suddenly getting proper typed arrays was a huge deal.
It was then discovered that JavaScript could manipulate these arrays and buffers at high speed, providing it only used a subset of the language. A subset that the JavaScript runtime could JIT compile more easily (turn into machine-code).
So what the EmScripten team did was to implement a bytecode based virtual-machine in Javascript, and then they compile C/C++ to bytecodes. I know, it’s a huge project, but the results speak for themselves — before WebAssembly, this was as fast as it got with JavaScript.
WebAssembly
WebAssembly is different from both vanilla JavaScript and Asm.js. First of all, it’s executed at high speed by the browser itself. Not like asm.js where these bytecodes were executed by JavaScript code.

Water is a fast, slick and platform independent IDE for Elements. The same IDE for OS X is called Fire. You can use RemObjects Elements from either Visual Studio or Water
Secondly, WebAssembly is completely JIT compiled by the browser or node.js when loading. It’s not like Asm.js where some parts are compiled, others are interpreted. WebAssembly runs at full speed and have nothing to do with traditional JavaScript. It’s actually a completely separate engine.
Out of all the options on the table, WebAssembly is the technology with the best performance.
Kits and strategies
The first thing you need to be clear about, is what you want to work with. The needs and requirements of a game developer will be very different from a system service developer.
Here are a couple of kits to think about:
- Mobile developer
- Implement your mobile applications using Oxygene, compiling for WebAssembly (Elements)
- RemObjects Remoting SDK for client / server communication
- Use Freepascal for vanilla JavaScript scaffolding when needed
- Service developer
- Implement libraries in Oxygene to benefit from the speed of WebAssembly
- Use RemObjects Data Abstract to make data-access uniform and fast
- Use Freepascal for boilerplate node.js logic
- Desktop developer
- For platform independent desktop applications, WebAssembly is the way to go. You will need some scaffolding (plain Javascript) to communicate with the application host – but the 99.9% of your code will be better under WebAssembly.
- Use Cordova / Phonegap to “bundle” your WebAssembly, HTML5 files and CSS styling into a single, final executable.
The most important part to think about when getting into JavaScript, is to look closely at the benefits and limitation of each technology.
WebAssembly is fast, wicked fast, and let’s you write code like you are used to from Delphi. Things like pointers etc are supported in Elements, which means ordinary code that use pointers will port over with ease. You are also not bound on hand-and-feet to a particular framework.
For example, EmScripten for C/C++ have almost nothing in terms of UI functionality. The visual part is a custom build of SDL (simple directmedia layer), which fakes the graphics onto an ordinary HTML5 canvas. This makes EmScripten a good candidate for porting games written in C/C++ to the web — but it’s less than optimal for writing serious applications.
Setting up the common tools
So far we have looked at a couple of alternatives for getting into the wonderful world of JavaScript in lieu of other languages. But what if you just want to get started with the typical tools JS developers use?

Visual Studio Code is a pretty amazing code-editor
The first “must have” is Visual Studio Code. This is actually a great example of what you can achieve with JavaScript, because the entire editor and program is written in JavaScript. But I want to stress that this editor is THE editor to get. The way you work with files in JS is very different from Delphi, C# and Java. JavaScript projects are often more fragmented, with less code in each file – organized by name.

TypeScript was invented by Anders Hejlsberg, who also made Delphi and C#
The next “must have” is without a doubt TypeScript. Personally im not too fond of TypeScript, but if ordinary JavaScript makes your head hurt and you want classes and ordinary inheritance, then TypeScript is a step up.
Next on the list is AssemblyScript. This is a post-processor for TypeScript that converts your code into WebAssembly. It lacks much of the charm and elegance of Oxygene, but I suspect that has to do with old habits. When you have been reading object-pascal for 20 years, you feel more at home there.
You will also need to install node.js, which is the runtime engine for running JavaScript as services. Node.js is heavily optimized for writing server software, but it’s actually a brilliant way to write services that are multi-platform. Because Node.js delivers the same behavior regardless of underlying operating system.
And finally, since you definitely want to convert your JavaScript and/or WebAssembly into a stand-alone executable: you will need Adobe Phonegap.
Visual Studio
No matter if you want to enter JavaScript via Elements or something else, Visual Studio will save you a lot of time, especially if you plan on targeting Azure or Amazon services. Downloading and installing the community edition is a good idea, and you can use that while exploring your options.
When it comes to writing system services, you also want to check out NPM, the node.js package manager. The JavaScript ecosystem is heavily package oriented – and npm gives you some 800.000 packages to play with free of charge.
Just to be clear, npm is a shell command you use to install or remove packages. NPM is also a online repository of said packages, where you can search and find what you need. Most packages are hosted on github, but when you install a package locally into your application folder – npm figures out dependencies etc. automatically for you.
Books, glorious books
Last but not least, get some good books. Seriously, it will save you so much time and frustration. Amazon have tons of great books, be it vanilla JavaScript, TypeScript, Node.js — pick some good ones and take the time to consume the material.
And again, I strongly urge you to have a look at Elements when it comes to WebAssembly. WebAssembly is a harsh and barren canvas, and being able to use the Elements RTL is a huge boost.
But regardless of path you pick, you will always benefit from learning vanilla JavaScript.
Two new groups in the Developer family
Delphi Developer is a group on Facebook that have been going strong for 12+ years. It was one of the first groups on Facebook, created the same week that Facebook allowed groups. With that group well established, it’s time to expand and clean up the feed.
Last month I introduced a new group, RemObjects Developer, which is a group for developers that use RemObjects components, like the Remoting SDK, Data Abstract and/or Hydra – but more in particular, developers using Oxygene, C#, Swift, Java or Go via Elements (RemObjects compiler toolchain).
Two new groups
To further simplify syndication, and clean up the feeds (which so far has been a pot-purrey of many topics, dialects and products) an additional two groups is now in place:
Obviously there will be some overlapping. Since FPC and Delphi has much in common and are for the most part compatible, some news will be shared between those groups. But all in all this is to clean up the newsfeed which has so far been a mix and match of everything.

Simple overview of the groups
Node.js Developer is not meant to be purely about vanilla JavaScript. Node.js is ultimately a JavaScript runtime-engine. Which means you can use it to run or host WebAssembly libraries (as produced by Oxygene), or generate code via DWScript or Freepascal. You can think of it as a service-host if you like.
So if you are writing WebAssembly applications using Elements, then the node.js group will no doubt be interesting too. Same goes for DWScript users, Smart Pascal users and Freepascal users – providing web tech is what they like.
What is this Quartex Components?
It’s easier to manage multiple groups if you attach them to a parent-page. So if you wonder why all the groups says “by Quartex Components”, that is just a top-level page that helps me deal with with syndication. For some reason Facebook’s API only works for pages, not groups. So it’s impossible to auto-import news (for example) without a page.
The name, “Quartex Components” is ultimately the name of my personal company. I used to produce security components for Delphi, but decided to open-source those for the community.
So Quartex Components is just an organizational element.
You must be logged in to post a comment.