Desktop Info can display data collected by an external collector. A collector is a stand alone application. You can use any tool at your disposal to write a collector that’s capable of collecting the data you need, constructing XML and writing it to a Windows shared memory area. The XML format must follow the format below. The collector implementation details are left to the author.
Once you have a collector running and writing XML to shared memory, you simply add a COLLECTOR item to your config file. The name option is the name of the shared memory area to which your collector is writing. That’s it. Desktop Info will attempt to open the shared memory and if successful, read the XML and convert it to data. Any errors in reading the data are noted in the log when error or debugonerror is enabled.
Your collector can collect as much data as you need and stuff it into a single shared memory area. One or many COLLECTOR items can read from a single shared memory area but only one collector can write to a given shared memory area. A single collector may create multiple shared memory areas for different types of data.
Sample Delphi Collector
This is the complete text of the MainForm.pas for my sample Delphi 10.3 collector. It has 2 constant strings containing the name used for the shared memory area and a sample XML. The shared area name is case sensitive. In the FormCreate, the shared memory area is created. This stays open for the life of the application. In the Button1Click event, the XML is written to the shared memory. In between these two things is where you collect your data and convert it to XML. In the FormClose event, the shared memory area is closed.
My little sample collector has a memo on the main form so I can experiment with the XML, the data, the data types etc, then I click the button to write that XML to the shared memory.
I have set the area size to 4096. This seems to be the page granularity of memory. If you need a bigger area just make this bigger. The actual area size created will be rounded up to the next whole page. Although it’s not documented anywhere, it seems the security attributes is absolutely necessary to create the shared memory. I’m using WideChar because that seems to be compatible with C++.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
unit MainForm; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private declarations } procedure WriteData(data: String); public { Public declarations } end; var Form1: TForm1; implementation var HSharedArea: Cardinal; PSharedArea: Pointer; AreaSize : DWORD = 4096; SecAttr : TSecurityAttributes; pSecAttr: PSecurityAttributes; SecDesc : TSecurityDescriptor; const CollectorName = 'SampleCollector'; SampleXml = '<desktop_info>'#13#10+ ' <raw_values>'#13#10+ ' <row>'#13#10+ ' <data name="col1" type="3">250</data>'#13#10+ ' <data name="col2" type="5">115.739</data>'#13#10+ ' <data name="col3" type="256">Hello!</data>'#13#10+ ' </row>'#13#10+ ' </raw_values>'#13#10+ '</desktop_info>'; // data type is System variant type codes {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin // security attributes // Create a Null DACL, which is basically "wide-open" security. Access will never be denied. pSecAttr := nil; if (InitializeSecurityDescriptor(@SecDesc, SECURITY_DESCRIPTOR_REVISION) and SetSecurityDescriptorDacl(@SecDesc, TRUE, nil, FALSE)) then begin SecAttr.nLength := sizeof(SecAttr); SecAttr.lpSecurityDescriptor := @SecDesc; SecAttr.bInheritHandle := False; pSecAttr := @SecAttr; end; // create or open a named mapping object HSharedArea := CreateFileMapping(INVALID_HANDLE_VALUE, pSecAttr, PAGE_READWRITE, 0, AreaSize, PChar(CollectorName)); // create the mapped view // offset=0, size=all if (HSharedArea <> NULL) and (HSharedArea > 0) then PSharedArea := MapViewOfFile(HSharedArea, FILE_MAP_WRITE, 0, 0, 0); Memo1.Text := SampleXml; end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin UnmapViewOfFile(PSharedArea); CloseHandle(HSharedArea); end; procedure TForm1.Button1Click(Sender: TObject); begin WriteData(Memo1.Text); end; procedure TForm1.WriteData(data: String); Var ws : WideString; pw : PWideChar; begin if assigned(PSharedArea) then begin // zero out the shared memory so there's no hangover from previous data FillChar(PSharedArea^, AreaSize, 0); // now put the data in, widechar is compatible with C++ ? ws := data; pw := PWideChar(ws); CopyMemory(PSharedArea, pw, Length(ws)*SizeOf(WideChar)); end; end; end. |
Desktop Info Configuration
With your collector running and writing XML to the shared memory, add a COLLECTOR item to your Desktop Info ini file.
1 2 |
[items] COLLECTOR=name:SampleCollector, interval:1, display:%1 |
If all is working, Desktop Info will display your collector data.
You can have many items displaying multiple shared memory areas or many items displaying the same area but for each shared memory area you need at least one COLLECTOR item.
1 2 3 4 |
[items] COLLECTOR=name:SampleCollector, interval:1, display:%1 %2 COLLECTOR=name:SampleCollector, interval:1, display:%3 COLLECTOR=name:JoeCollector, interval:1, display:%joe_data% |
XML Format
The name attribute is the column name that can be used in the display template. The type is the data type listed below.
1 2 3 4 5 6 7 8 9 |
<desktop_info> <raw_values> <row> <data name="col1" type="3">25</data> <data name="col2" type="258">Hello World</data> <data name="col3" type="6">40.55</data> </row> </raw_values> </desktop_info> |
Data Types
Numeric values may be formatted in the display template and charted in the usual way. Strings may contain expressions and/or user variables.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
2 = smallint 3 = integer 4 = single 5 = double 6 = currency 7 = date (TDateTime) 8 = bstr 11 = bool 16 = shortint 17 = byte 18 = word 19 = longword / uint32 20 = int64 21 = uint64 256 = pascal string 258 = unicode string |