chkaufmann
Members-
Content Count
158 -
Joined
-
Last visited
Everything posted by chkaufmann
-
When I use TStringList.SaveToFile() on a hidden text file, I get a create error. Even if I do this as administrator. So the only solution I see is to read the attributes with GetFileAttributes, then save the file and then use SetFileAttributes to hide the file again. Or do I miss something? Christian
-
When a user starts my application, I would like to know all groups assigned to that user. On the command line I can do this: dsquery user -samid "christian.kaufmann" | dsget user -memberof -expand Now I have a delphi function I could use to run this call and then parse the input. But before I start to implement that I would like to know: - Is a regular user allowed to call this in order to read that information? - Is there another way to get this info from the system? - Or are only administrators allowed to read this information? The background is, I would like to show the user a list of available projects based on the groups he is a member of. Christian
-
My application runs on a server as remote desktop application. Now I want a function to add files from a local the drive. The RDP file is configured to map local drives and I can see/select these in the file open dialog. Now I would like to limit this dialog to the clients local drives only. - Is there a global option for that? - Is it enough to set a OnFolderChanging event and check the path to be a local drive? - Are there other things todo? Since the file open dialog is almost a complete Explorer today, I would like to limit functionality to open/load a file. Regards Christian
-
TFileOpenDialog - limit functionality in RemoteApp
chkaufmann replied to chkaufmann's topic in Windows API
My problem is, that user managment is done by an external company with quite expensive people. Not my choice, and I just try to make things user friendly with the part I do. One final question: The shared drives as UNC path look like \\tsclient\c\..... Is this "\\tsclient" a default by Microsoft in a terminal session or can this prefix be customized by the administrator? Christian -
TFileOpenDialog - limit functionality in RemoteApp
chkaufmann replied to chkaufmann's topic in Windows API
Yes I know. In the end it's more to help the user do use only his local drives to save/load files. Christian -
My application has to connect to another application. It waits for messages in XML format, delimited with SOH and EOT. I wrote a listener thread. So far it works, but I wonder, if this is the way to do it. Here is my code: - Is it correct to call ReadLn and then the thread is blocked until any data is sent by the server? - I cannot test the error handling with SendData because I don't succeed to deactivate the server in the moment when the data is sent. Will it work that way? Or should I check another property of my TIdTcpClient to see if the server was deactivated? - Do I need more cleanup if I destroy my TTsClient object? Christian type TTsClientThread = class(TThread) strict private FClient : TIdTCPClientCustom; FMessages : IBSQueue<IBSXmlElement>; FPartMsg : String; strict protected procedure Execute; override; public constructor Create(AClient: TIdTCPClientCustom; const AMessages: IBSQueue<IBSXmlElement>); end; TTsClient = class(TIdTCPClientCustom) strict private FListener : TTsClientThread; FMessages : IBSQueue<IBSXmlElement>; public constructor Create(const AIpAddress: String; APort: Integer); destructor Destroy; override; function IsAlive: Boolean; function Pop(var AMessage: IBSXmlElement): Boolean; procedure SendData(const AMessage: IBSXmlElement); end; { TTsClientThread } constructor TTsClientThread.Create(AClient: TIdTCPClientCustom; const AMessages: IBSQueue<IBSXmlElement>); begin FClient := AClient; FMessages := AMessages; inherited Create; end; procedure TTsClientThread.Execute; var s : String; begin while not Terminated do begin try s := FClient.Socket.ReadLn(Char.EOT, IndyTextEncoding_UTF8).Substring(1); if not s.IsEmpty then FMessages.Append(NewBSXmlFromString(s)); except on E: EIdSocketError do begin Terminate; Exit; end; end; end; end; { TTsClient } constructor TTsClient.Create(const AIpAddress: String; APort: Integer); begin inherited Create(nil); FMessages := TBSGenerics.GenQueue<IBSXmlElement>; Host := AIpAddress; Port := APort; Connect; FListener := TTsClientThread.Create(Self, FMessages); end; destructor TTsClient.Destroy; begin FListener.Free; inherited; end; function TTsClient.IsAlive: Boolean; begin Result := not FListener.Terminated; end; function TTsClient.Pop(var AMessage: IBSXmlElement): Boolean; begin Result := FMessages.Pop(AMessage); end; procedure TTsClient.SendData(const AMessage: IBSXmlElement); begin try Socket.Write(IndyTextEncoding_UTF8.GetBytes(Char.SOH + AMessage.AsXmlDataString([]) + Char.EOT)); except on E: EIdSocketError do FListener.Terminate; end; end;
-
Thanks for the reply. I have one final question. First I tried with this call: var tmp : TIdBytes; FClient.IOHandler.ReadBytes(tmp, -1, False); FPartMsg := FPartMsg + IndyTextEncoding_UTF8.GetString(tmp); .... Here I noticed, that if ReadBytes had nothing to read, tmp was not reset to length zero, but once some bytes could be read, tmp was set to the number of bytes. What is the reason for that? Christian
-
Generic record helper for enumeration types
chkaufmann posted a topic in RTL and Delphi Object Pascal
For many enumeration type I have I define the same set of methods. Here is an example: TBSSwGender = (sgeNone, sgeMen, sgeWomen, sgeMixed); TBSSwGenderHelper = record helper for TBSSwGender public function AsInteger: Integer; function Code: String; function Name: String; end; function TBSSwGenderHelper.AsInteger: Integer; begin Result := Ord(Self); end; function TBSSwGenderHelper.Code: String; begin Result := '#Enumeration.BSSwGenderCode%d'.Fmt([AsInteger]).Translate; end; function TBSSwGenderHelper.Name: String; begin Result := '#Enumeration.BSSwGenderName%d'.Fmt([AsInteger]).Translate; end; Now my question is, instead of writing the same code for all types. So something like this: TBSEnumHelper<T> = record helper for T function AsInteger: Integer; ... end; TBSEnumHelper<TBSSwGender> = record helper for TBSSwGender Christian -
Enumeration Type as Parameter
chkaufmann replied to chkaufmann's topic in RTL and Delphi Object Pascal
This is what I did. The builder class itself has no RTTI. But I created a generic record. IBSLookupElements is a simple collection of (Key, Caption, ParentKey) elements. Fmt() and Translate() are string helper methods for Format and language specific translations. Then TBSCatalogBuilderSimple is a general subclass of my TBSCatalogBuilder class. Here is my record definition: type TBSCatalogBuilder<T> = record strict private FCatalogId : Integer; FDefault : Integer; FName : String; FTextIdent : String; FTypeData : PTypeData; public constructor Create(ACatalogId: Integer; const AName: String); function Gen: TBSCatalogBuilder; function SetDefault(const AValue: T): TBSCatalogBuilder<T>; function SetTextIdent(const AValue: String): TBSCatalogBuilder<T>; end; { TBSCatalogBuilder<T> } constructor TBSCatalogBuilder<T>.Create(ACatalogId: Integer; const AName: String); var i : PTypeInfo; begin i := TypeInfo(T); Assert(i.Kind = tkEnumeration, 'Passed type is not an enumeration.'); FTypeData := GetTypeData(i); FCatalogId := ACatalogId; FDefault := 0; FName := AName; end; function TBSCatalogBuilder<T>.Gen: TBSCatalogBuilder; var tmp : IBSLookupElements; begin tmp := NewBSLookupElements; for var ix := (FTypeData.MinValue + 1) to FTypeData.MaxValue do tmp.AddOrSet(FTextIdent.Fmt([ix]).Translate, ix); Result := TBSCatalogBuilderSimple.Create(FCatalogId, FName, tmp, FDefault); end; function TBSCatalogBuilder<T>.SetDefault(const AValue: T): TBSCatalogBuilder<T>; begin FDefault := TValue.From<T>(AValue).AsOrdinal; Result := Self; end; function TBSCatalogBuilder<T>.SetTextIdent(const AValue: String): TBSCatalogBuilder<T>; begin FTextIdent := AValue + '%d'; Result := Self; end; Then the usage of the helper looks like this: type TElmeSchlafmodus = (esmNone, esmKeinSchlafmodus, esmTeilzeit, esmVollzeit); ADef.RegisterCatalog(TBSCatalogBuilder<TElmeSchlafmodus>.Create(CAT_ELME_BETRIEB_SCHLAFMODUS, 'Schlafmodus') .SetTextIdent('#ElmeBetrieb.Schlafmodus') .SetDefault(esmVollzeit).Gen); Thanks for all help. Christian -
Enumeration Type as Parameter
chkaufmann replied to chkaufmann's topic in RTL and Delphi Object Pascal
Ok, here is the long story. In my system I define catalogs that can be used as lookup for values. There are complex catalogs with dynamic content that can be edited by the users. And there are simple catalogs that are hard coded in my application. For these hard coded catalogs I have builder class: TBSCatalogBuilder = class abstract strict protected function AddItem(AItemId: Integer; const AName: String; AIsDefault: Boolean = False; const AParentItem: IBSCatalogItem = nil): IBSCatalogItem; procedure DoBuildItems; virtual; abstract; public constructor Create(ACatalogId: Integer; const AName: String; AOptions: TBSCatalogOptions = []); end; So to define a catalog I create a subclass of TBSCatalogBuilder, overwrite the DoBuildItems method and call AddItem several times to define the catalog. Now some hard coded catalogs just represent the items of an enumaration type and instead of creating a subclass I would like one class with a constructor that takes the type of the enumeration and a second parameter with the default value. Right now the definition looks like this: TBSCatalogBuilderSetType = class sealed(TBSCatalogBuilder) strict protected procedure DoBuildItems; override; final; public constructor Create(ACatalogId: Integer; const AName: String; ASetType: PTypeInfo; ADefault: Integer = 0); end; and then I can use it like this: type TBSColumnType = (ctUnknown, ctBoolean, ctBytes, ctDateTime, ctFloat, ctInteger, ctMemo, ctString, ctGuid); begin myColumnTypeCatalog = TBSCatalogBuilderSetType.Create(1, 'Column Types', TypeInfo(TBSColumnType), Ord(ctString)); end; This works and it does what I want, but the TypeInfo() and the Ord() just don't look nice and I was wondering if there is not a way to avoid these. And I didn't want to define a generic class because like this the code is duplicated for every catalog. But maybe this is the only way to do it: TBSCatalogBuilderSet<T> = class(TBSCatalogBuilder) public constructor Create(ACatalogId: Integer; const AName: String; ADefault: T); end; Christian -
Enumeration Type as Parameter
chkaufmann replied to chkaufmann's topic in RTL and Delphi Object Pascal
It's just a question of readability. My version works fine, but now I would like to avoid these TypeInfo() and Ord() when I call Foo(). And if I can validate the second parameter to be a valid member of the first (tkEnumeration) Parameter, this would be a nice additional check. Christian -
Enumeration Type as Parameter
chkaufmann replied to chkaufmann's topic in RTL and Delphi Object Pascal
Yes "generic" is maybe the wrong word because I don't mean a generic method. Maybe I should have said untyped or typeless. I would just like to call it like this: Foo(TSet1, a13); Foo(TSet2, a21); Christian -
I have two applications that communicate in a LAN using UDP. Both applications have TIdUDPClient and a TIdUDPServer. Until now the user must configure the port and the IP address. Now I would like a solution where client computers "find each other" automatically. What is the best way to do that? Regards Christian
-
In my Windows applications I use VirtualTreeview for grids with inplace editors. Sometimes I load up to several 10'000 rows (nodes). For this I use rather light node classes. When I look at TTreeviewItem in FMX, then each node is a control and I don't think, this will work the same way. So are there alternatives. I need - tree check functionality (simple, tristate) - multiple columns - inplace editors - lazy load for child nodes Christian
-
Hi, I have an application that still uses an MS Access as database. With the library I have I can run it on other databases as well since all requests are pure SQL (no TTable or things like that). Now I'm looking for a replacement and I wonder, if there is a solution that could be used for cloud and offline. The databases are not that big (about 25 tables, maximum 25'000 records each). But each customer may have up to 50-100 databases. Ideal would be a solution where the database could be in the cloud, but there is an offline copy in case the internet connection is broken, so I can continue to work. When I'm back online, all changes would be synchronized to the server. Now I know there is probably no easy solutions, but I'm open for ideas on how to find a good solution. Christian
-
Hi, I just fixed an error in the following function. I had to add the last line where I assign tmp to Result. Now I wonder, why I didn't get a compiler warning like "return value of function might be undefined.". I get such errors in the same project with other methods, so the options are set correctly. Christian class function TLnxResult.GenForEvent(AData: TLnxObject; AEventId, AAgegroupId: Integer): IBSEnumerable<IBSSwLenexResult>; var data : TLnxAbstractObject; clubs : TLnxCollection; tmp : IBSList<IBSSwLenexResult>; begin tmp := TBSGenerics.GenList<IBSSwLenexResult>; data := AData; while Assigned(data) and (data.Tag <> lxCOMPETITION) do data := data.Parent; clubs := TLnxCollection(TLnxObject(data).ChildObject[lxCLUBS]); if Assigned(clubs) then for var ixC := 0 to clubs.ChildCount - 1 do begin for var e in TLnxEntryResult.GenForClub(lxRESULTS, TLnxCollection(clubs.Objects[ixC].ChildObject[lxPERSONS]), AEventId, AAgegroupId) do tmp.Add(e as IBSSwLenexResult); for var e in TLnxEntryResult.GenForClub(lxRESULTS, TLnxCollection(clubs.Objects[ixC].ChildObject[lxRELAYS]), AEventId, AAgegroupId) do tmp.Add(e as IBSSwLenexResult); end; tmp.Sort(function(const AResult1, AResult2: IBSSwLenexResult): Integer begin Result := AResult1.Place(nil) - AResult2.Place(nil); if Result.IsZero and (AResult1.Place(nil) = BSSW_MAX_PLACE) then Result := AResult1.Status.AsInteger - AResult2.Status.AsInteger; end); Result := tmp; // <<<< this line was missing. end;
-
So just that I get it right: Because my return value is an interface type, the compiler adds something like this as first statement of the method: Result := nil; Chrisitan
-
In 10.3 (Rio), class constructors are implemented the same way as initialization sections of units. This means, I have no control about the order. So if I create an object of my class in an initialization section, I cannot be sure, that the class constructor for this class was already called. Just wondering: is this problem fixed in 10.4? Or is it still done the same way? Christian
-
Class Constructor in Delphi 10.4
chkaufmann replied to chkaufmann's topic in RTL and Delphi Object Pascal
The problem is to create a reproducable case. The order of initialization sections and class constructor is at least partial random. So things work fine but if you add a unit or change one of the uses in a project, the order is different. So I don't use class constructor anymore. -
Hi, The color select dialog in Delphi (TColorDialog, TColorBox) looks quite old and keeping multiple custom colors available doesn't really work. Does somebody have a recommendation to replace it? Christian
-
In my application I have a TApplicationEvents object and there I use the OnMessage event. I was wondering why this event is called all the time even then when I don't touch the mouse and/or the keyboard. Now I noticed that WM_MOUSEMOVE are sent all the time. Is this normal - probably not - but how can I find out, where these messages come from. As soon as I move the mouse cursor outside of my applications window the messages stop. Christian
-
Releation between entity types
chkaufmann posted a topic in Algorithms, Data Structures and Class Design
We built our own framework where we can define different entity types. Each Entity Type has 1-n values. Then we allow different relation between entity types: - Parent / Child - Reference - Link (m:n relation) At first glance a parent/child relation is the same as a mandatory reference. But somehow it feels, it's not and I try to find the rules when to choose parent/child and when to choose reference. Can someone point me to appropriate reading? Thanks Christian -
Releation between entity types
chkaufmann replied to chkaufmann's topic in Algorithms, Data Structures and Class Design
No. In my approach is I have only two tables in the SQL database: ENTITY and ENTITYVALUE. Basically each object has one record in ENTITY with general values (id, guid, ownerid, created, modified) and then it has one record for each value in ENTITYVALUE. Like this, the database is very generic and the meta information is in the code only. Christian -
Hi, from time to time I run into an endless AV when running grep from GExperts. Here is the stack trace. I'm not sure if this is a GExperts or a DevExpress problem: [5005FD90]{rtl260.bpl } System.TObject.InheritsFrom (Line 18239, "System.pas" + 6) + $0 [5005FBE2]{rtl260.bpl } System.@IsClass (Line 17942, "System.pas" + 1) + $8 [0B4ED18C]{themeloader260.bpl} System. + $0 [0B4ED959]{themeloader260.bpl} Idetheme.Vclstyledialogshooks.TTabControlIDEDialogStyleHook.Paint + $2D [50E05577]{vcl260.bpl } Vcl.Themes.TCustomStyleServices.DrawParentBackground (Line 2592, "Vcl.Themes.pas" + 1) + $F [50E05591]{vcl260.bpl } Vcl.Themes.TCustomStyleServices.DrawParentBackground (Line 2598, "Vcl.Themes.pas" + 1) + $D [0B4E61F2]{themeloader260.bpl} Idetheme.Vclstylehooks.TTabControlIDEStyleHook.PaintBackground + $82 [0B4D8D17]{themeloader260.bpl} Idetheme.Vclstylehooks.TIDEStyleHook.WMPaint + $EF [0B4D8E82]{themeloader260.bpl} Idetheme.Vclstylehooks.TIDEStyleHook.WndProc + $7A [0B4D97B0]{themeloader260.bpl} Idetheme.Vclstylehooks.TMouseTrackControlIDEStyleHook.WndProc + $0 [0B4E6AB2]{themeloader260.bpl} Idetheme.Vclstylehooks.TTabControlIDEStyleHook.WndProc + $A [0B4D8924]{themeloader260.bpl} Idetheme.Vclstylehooks.TIDEStyleHook.HandleMessage + $A0 [0B511493]{themeloader260.bpl} Idetheme.Stylemanager.TIDEThemeStyleEngine.HandleMessage + $97 [0B511C96]{themeloader260.bpl} Idetheme.Stylemanager.TIDEThemeStyleEngine.UnRegisterSysStyleHook + $DA [50D197BC]{vcl260.bpl } Vcl.Controls.TWinControl.WndProc (Line 10122, "Vcl.Controls.pas" + 10) + $48 [50CF65D7]{vcl260.bpl } Vcl.Graphics.FreeMemoryContexts (Line 7138, "Vcl.Graphics.pas" + 12) + $8 [50D19314]{vcl260.bpl } Vcl.Controls.TWinControl.MainWndProc (Line 9977, "Vcl.Controls.pas" + 3) + $6 [501796A4]{rtl260.bpl } System.Classes.StdWndProc (Line 17932, "System.Classes.pas" + 11) + $2 [1D14BADA]{DevEx_Main.bpl} Cxcontainer.TcxCustomInnerListBox.DoGetGestureOptions + $AFE [1D08A9E1]{DevEx_Main.bpl} Dxhooks. + $0 [50E6EC33]{vcl260.bpl } Vcl.Forms.TApplication.ProcessMessage (Line 10747, "Vcl.Forms.pas" + 23) + $1 [50E6EC5E]{vcl260.bpl } Vcl.Forms.TApplication.ProcessMessages (Line 10769, "Vcl.Forms.pas" + 1) + $4 I'm using Delphi 10.3.2 and GExperts 1.3.13.77. Christian
-
I have a TEdit that is a bit higher than normal in order to align it with other controls. Then I wanted to center the text vertically but there is no such property and the only solution I found is to overwrite the Paint method in a subclass. Is this really the case? Or did I miss something? Christian