Jump to content

aehimself

Members
  • Content Count

    1090
  • Joined

  • Last visited

  • Days Won

    23

Everything posted by aehimself

  1. aehimself

    Need to convert varbinary to hex string in a dll

    BinToHex should work, but you can give this a try: Function MyBinToHex(inBinary: TArray<Byte>): String; Var b: Byte; Begin Result := '0x'; For b In inBinary Do Result := Result + IntToHex(Ord(b), 2); End; ..,in older Delphi's: Function MyBinToHex(inBinary: Array Of Byte): String; Var a: Integer; Begin Result := '0x'; For a := Low(inBinary) To High(inBinary) Do Result := Result + IntToHex(Ord(b), 2); End;
  2. aehimself

    Delphi 10.3 Rio on Windows Server 2008 R2

    Talking about a virtual environment, especially Xen; I'd give the how-to written by @Dalija Prasnikar a shot: https://dalijap.blogspot.com/2019/05/disable-delphi-rio-ide-theme.html There were multiple reported painting issues solved by disabling theming of the IDE.
  3. aehimself

    License key system

    I am strictly talking about security. By not a valid license I don't mean an expired; a crafted one which is known to be not from the author. As for code execution, it's not that easy. Of course if you are corrupting with (or you did not initialize your buffer, and it contains) the exact binary representation of a call to DeleteFile - it will work. procedure TForm1.Button1Click(Sender: TObject); Type TProcedure = Procedure; PProcedure = ^TProcedure; Var p: PProcedure; buf: Pointer; begin GetMem(buf, 1024); Try p := buf; p^; Finally FreeMem(buf); End; end; Project Project1.exe raised exception class $C0000005 with message 'access violation at 0x00000000: read of address 0x00000000'. Execution denied. Same, if you try to execute a differently allocated memory area: procedure TForm1.Button2Click(Sender: TObject); Type TProcedure = Procedure; PProcedure = ^TProcedure; Var p: PProcedure; x: TObject; begin p := Addr(x); p^; end; Project Project1.exe raised exception class $C0000005 with message 'access violation at 0x02f788b0: write of address 0x060904ec'. No luck. Out-of-bounds? procedure TForm1.Button3Click(Sender: TObject); Type TProcedure = Procedure; PProcedure = ^TProcedure; Var p: PProcedure; begin p := Pointer($ABABABAB); p^; end; Project Project1.exe raised exception class $C0000005 with message 'access violation at 0x005fd31b: read of address 0xabababab'. The OS is attempting to take measures against this, and if it's possible (somewhat how iPhone / PS4 jailbreaks used to work until they patched their browsers) - you found an exploit. With not invasive memory corruption you'll turn some output Chinese, or crash the application at a point. Do it carelessly, and you can face charges.
  4. For a long time I am using a custom settings class in most of my applications to store it's data. They all have a .AsJSON property to export / import everything, which is automatically called by .Load and .Save. It's working perfectly. The only drawback is that I had to write the getter and setter methods for each instance, even if it's only a sub-class of the main settings. And it was getting tiring. I started to experiment with RTTI to make the class detect it's own properties in the getter and setter so I finally can forget about these in general situations. Exporting works like a charm; I only could not properly handle TLists but I wrote a custom list wrapper for the rescue. The importing (loading from JSON) part caused some headache, which I can't seem to solve. One part is left, if the property / field is an array type. The inner helper method looks like this: Function TApplicationSettings.JSONToValue(inJSON: TJSONValue; inTypeInfo: PTypeInfo): TValue; Var enum: TJSONArray.TEnumerator; valarray: TArray<TValue>; a: Integer; Begin Result := TValue.Empty; Case inTypeInfo.Kind Of [...] tkArray, tkDynArray: Begin enum := TJSONArray(inJSON).GetEnumerator; SetLength(valarray, TJSONArray(inJSON).Count); If Assigned(enum) Then Try a := 0; While enum.MoveNext Do Begin valarray[a] := JSONToValue(enum.Current, inTypeInfo); Inc(a); End; Finally enum.Free; End; Result := TValue.FromArray(inTypeInfo, valarray); End; End; End; ..and the outer helper: Procedure TApplicationSettings.JSONToRttiMember(inJSON: TJSONValue; inRttiMember: TRttiMember); Var val: TValue; pi: PTypeInfo; ppi: PPropInfo; Begin If Not Assigned(inJSON) Then Exit; ppi := GetPropInfo(Self, inRttiMember.Name); If Not Assigned(ppi) Then Exit; pi := ppi^.PropType^; val := JSONToValue(inJSON, pi); If val.IsEmpty Then Exit; If inRttiMember Is TRttiProperty Then (inRttiMember As TRttiProperty).SetValue(Self, val) Else If inRttiMember Is TRttiField Then (inRttiMember As TRttiField).SetValue(Self, val); End; This most possibly where my mistake is, as GetPropInfo always returns nil. Before this solution I passed the (PropertyType / FieldType).TypeKind as a parameter to the inner helper, but in that case I won't have access to PTypeInfo, which is needed for the TValue.FromArray. So the question is; how can I extract the PTypeInfo of an RTTI field / property? Because the code above doesn't, that's for sure πŸ™‚
  5. I saw handle in the list; I just quickly passed by it as it usually means something different... Thank you!
  6. And this is something I will never EVER argue about. For me it also makes the code harder to read, as I already got used to Create - Try - Finally - Free - End. I'll always be alarmed if this pattern is not followed. In my case unfortunately it's a bit different, as we have (I'd call them as) protocol descriptors in our custom dataset descendants, no direct SQL queries. But I hate setting the variables to nil before.
  7. If you mean you have to set your variable to nil before first calling this method there's no question about it. As for freeing, that should be handled in the same method, which calls this helper. Like... Var myquery: TFDQuery Begin myquery := nil; Try createMyQuery(myquery, 'SELECT * FROM USERS'); [...] Finally myquery.Free; End; End; What I mean is, whether if var is declared in the definition of createMyQuery or not; it will not leak and will function correctly, as "myquery" will be passed as a reference (the object itself) and not a copy of it. Edit: I'm an idiot. Instead of asking I could have made a test case to confirm. Will come back soon with my results. Edit-edit: Disregard everything. Var is needed. Procedure TForm1.SLCreateAdd(inSL: TStringList); Begin If Not Assigned(inSL) Then inSL := TStringList.Create; inSL.Add('Hello'); inSL.Add('World'); End; procedure TForm1.Button1Click(Sender: TObject); Var sl: TStringList; begin sl := nil; Try SLCreateAdd(sl); ShowMessage(sl.Count.ToString); Finally sl.Free; End; end; Causes a nullreference exception and leaks a TStringList object. Procedure TForm1.SLCreateAdd(Var inSL: TStringList); Begin If Not Assigned(inSL) Then inSL := TStringList.Create; inSL.Add('Hello'); inSL.Add('World'); End; does not. I am obviously wrong with my memories and most probably used Var in my helper πŸ™‚
  8. You are absolutely right in this, I'm always declaring local variables though (and as it's my helper I'll not call it with nil) it seems to be irrelevant in my case. My real surprise is/was the leaking part. If my memory doesn't cheat and class instances are indeed passed as references to methods, there should not be any leaks in the above code, with var or not.
  9. aehimself

    License key system

    As network traffic is really easy to be misdirected, I am strongly against network-based authentication. As @Sherlock pointed out, they will simply fail to launch (or fall back to demo mode) in most of the real-world customer scenarios, where networks are controlled as they should be. Local license authentication is the way to go in my opinion, but there is no fool-proof way. Everything can be (and if it worth, will be) hacked no matter what. You only can make the job of the pirate harder with obfuscation, fake no-op assembly blocks, custom multi-level encryption, on-the-fly method assignments and so on. One thing for sure, delay checking the license and NEVER use something like If Not TMyLicense.IsValid Then Halt as on assembly level that's a modification of one JMP to bypass everything. I started to learn the proper use of pointers and if my license is not valid, I'm simply corrupting memory on purpose. It might (that's the beauty in it, it's not guaranteed) start to crash or malform data at the most random places / times. If you hide it well enough, even the hacker might think that it's a piece of junk and does not worth the effort...
  10. aehimself

    Where did I come from

    I am actively using this solution (usually with an atomic value) but it always felt hacky. Strange to see that it seems to be THE way. At least I did not write smelly code. At least in this part... πŸ™‚
  11. I thought class instances are always passed as references (pointers to the actual instance)...? I also do have a similar method to create and initialize the dataset and I think I didn't use var there. I do have to doublecheck it; I don't have access to that piece of code right now.
  12. aehimself

    Threading question

    Holy mess, I skipped only one day with my gadget addiction and I came back to see my E-mail inbox exploded! As it was mentioned countless times, you must get a stack trace of where the error happens. Without that most probably we are all looking at the wrong place. Far from ideal, but SendMessage works in this scenario. I doubt that Win7 - Win10 will be an issue either; I am testing most of my code with everything from Windows 2000 and up and so far only new features (which does not exist in earlier editions) caused headache. And just because it's a framework, don't be afraid to touch it! At work we have a similar setup - part of our legacy app's code is used as a base for many other applications. I started to sort out some basic leaks and you can not imagine what was buried deep below. A framework is the nothing but code, which can contain bugs even if thousands of other applications are using it. If you are absolutely sure you can not get a stack trace, at least pollute your methods with debug messages using a new logger with proper synchronization. When the AV occures, just look which method started and did not finish. Alas, using proper synchronization in your new debug logger might solve the synchronization issue (if it is a synchronization issue after all...) with those tiny-tiny delays. So yeah, reinventing the wheel is not a smart thing to do, if we have MadExcept and similar.
  13. aehimself

    JSON woes

    I'm using the standard System.JSON library. It supports everything I needed until now, except merging.
  14. aehimself

    Threading question

    Read of address with a low number means a nullpointer exception; the code is attempting to access an already freed / uninitialized object. Did you manage to catch where the AV is raised or you just see it in the logs? I was suspecting that the thread's "stat" variable is overwritten and therefore relocated; but it's unlikely because 1, it would show a random address, not 1. 2, thanks to @Anders Melander I remembered that SendMessage does not return until the message is processed. So unless "stat" can be written from the outside...
  15. Yes, yes and yes. I have a custom TThread descendant, which sets it's own name to it's classname. Makes debugging a lot more easy than hunting for IDs.
  16. aehimself

    OS Updates

    I used the WMI method as it was easier and I already had wrappers for it. Just SELECT * FROM Win32_QuickFixEngineering in ROOT\CIMV2.
  17. Good to know; thanks for sharing, I'll try to keep this in mind if I'll have to use FileStream-s one day!
  18. fmCreate, according to Delphi help = Create a file with the given name. If a file with the given name exists, override the existing file and open it in write mode. I don't use FileStreams that often, but won't you effectively null the contents...? I would go the old-school way; however I have no idea if it would work: Var f: File; Begin If Not FileExists('myfile') Then Exit(False); {$I-} AssignFile(f, 'myfile'); Append(f); Result := IOResult <> 0; If Not Result Then CloseFile(f); End;
  19. aehimself

    Delphi Licensing

    Sometimes we had this when using License Server activation and installing from an .ISO image. I personally never faced this issue, but my colleague succeeded by starting the network installer, importing the .slip file and then using the .ISO installer. And I thought these should be the same...? 😐
  20. aehimself

    Why is ShowMesssage blocking all visible forms?

    Yes, makes sense; did not think of this. What you can do is to have a TFrame with basic functionality (placeholder for the monitor, invisible panel, etc) and add a "pop out" button there. When you click it, you simply create a TForm and move the current TFrame on that new form. Just, don't forget to free the tabsheet πŸ™‚ I was also experimenting with auto-docking tabsheets a while ago. When you drag them out they turned to forms automatically, and vice versa. Unfortunately though the performance was so bad (flickering and lagging) that I abandoned the idea. Most probably I was doing something wrong; you can look in this direction too instead of a pop-out button. Edit: Seems pretty easy, I don't know what I messed up before. Edit-edit: It works, but the flickering is still there when dragging around the "child" window; even if DoubleBuffered is on. If you don't want to watch the video: Set DockSite of the PageControl to True. Set DragKind of the "child" form to dkDock and in the OnClose event set the Action to caFree. Then, create your "child" forms like this: Var f: TForm2; begin f := TForm2.Create(Application); f.ManualDock(PageControl1); f.Show; End;
  21. aehimself

    Why is ShowMesssage blocking all visible forms?

    Talking from an end-user perspective I'd go insane if one application would open tens of forms. I have one screen only and it will be polluted within seconds. What I would do is to have only one form, with a list on the left and a PageControl on the right. Instead of forms, create a new TabSheet for the monitors. When there is an alert, you can change the ImageIndex property to change the icon of the tab sheet; signaling the user that attention is needed. As for the "messages" I would put an invisible panel inside every tab sheet, on top of everything. It would contain only a TMemo and a button to dismiss. This way if multiple alerts are generated without interaction, you can append it to the memo instead of having 3-4 popup windows for a single monitor.
  22. Now this applies only to Windows as I'm not working with Linux machines, but the Windows Computer Browser service is basically collecting a bunch of broadcasted information and then others connect to it via TCP (source). If the activity-based startup is locked to an IP it will not be triggered by broadcast messages; the rest of the communication will be between other PCs and the current directory master. Alas, this only applies for standard services. My program attempting to ping this IP to see if it's alive or not will trigger the power on for sure.
  23. If File Explorer is the standard Windows file management application which pops up when you click on My Computer / This PC; and you just type ftp://host_or_ip_of_device it's hardwired in the shell for sure. I am also certain that Microsoft did not maintain this Client as they saw the future in WebDav; not FTP; but to be honest it's irrelevant in your case. You said your only issue is that your device is inserting a null character at the end of each file upon listing, which you save to a file. I'm not sure if Delphi is handling #0 characters in strings since Unicode; but you can try: Var sl: TStringList; a: Integer; Begin sl := TStringList.Create; Try sl.Delimiter := #0; sl.LoadFromFile('MyList.lst'); For a := 0 To sl.Count - 1 Do // Do something with the file at sl[a] Finally sl.Free; End; End; Or... Uses System.IOUtils; Var sa: TArray<String>; fname: String; Begin sa := TFile.ReadAllText('MyList.lst').Split([#0]); For fname In sa Do // Do something with the file in fname End; and then using any FTP client component you issue a download request. Edit: It does. Var sa: TArray<String>; fname: String; Begin sa := String('Hello' + #0 + 'World' + #0 + 'Zero char' + #0 + 'separation').Split([#0]); For fname In sa Do ShowMessage(fname); End; works, popping up one segment at a time; so it should do the trick.
  24. aehimself

    3 explorer running

    By the definition from MSDN (https://docs.microsoft.com/en-us/windows/win32/api/exdisp/nn-exdisp-ishellwindows) "IShellWindows interface Provides access to the collection of open Shell windows." [...] "A Shell window is a window that has been registered by calling IShellWindows::Register or IShellWindows::RegisterPending." Which means that if I want to, I can register any application as a shell window and you will see it in your collection. Maybe this is what you mean "hidden" as it is an instance of a shell window, which is NOT an Explorer process...? Edit: I suppose you already read this: "If the type is VT_UI4, the value of index is interpreted as a member of ShellWindowTypeConstants" ...which leads you here: "SWC_EXPLORER An Windows Explorer (Explorer.exe) window. SWC_BROWSER An Internet Explorer (Iexplore.exe) browser window. SWC_3RDPARTY A non-Microsoft browser window. SWC_CALLBACK A creation callback window. SWC_DESKTOP Windows Vista and later. The Windows desktop." So I suppose you are handling these cases in your code...?
  25. What periodic activity you mean? In normal circumstances a regular server (DHCP, DNS, File server) is not looking for offline clients. Regular protocols are not even looking for Clients at all as most of the actions are initiated by the Client.
Γ—