-
Content Count
2811 -
Joined
-
Last visited
-
Days Won
123
Everything posted by Remy Lebeau
-
Get path and GPS from a gallery picture for Android
Remy Lebeau replied to Colin S's topic in Cross-platform
When you capture an image to the library, FireMonkey does not give you access to the final image that is stored in the library, as you have noticed. And there is no guarantee that the image will have GPS information in it anyway (the user can disable that in the phone settings). So, generally, what you are asking for is not possible with TTakePhotoFromLibraryAction. You will likely have to either monitor the phone's library for new files, or else just access the phone's Camera and GPS APIs directly, -
HELP: Decoding of data stored in array of char - RFID tag's
Remy Lebeau replied to CRO_Tomislav's topic in VCL
Remember that PChar is PWideChar in Delphi 2009+, but the DSFIDAndUID data is returning an array of bytes, not characters, so the function should be declared as using PByte, not PChar, for that parameter: Function Inventory(var ComAdr : Byte; var State : Byte; var AFI : Byte; DSFIDAndUID : PByte; var CardNum : Byte; frmComPortindex : Integer): LongInt; stdcall; external 'RR3036.dll'; I would recommend this approach as well. However, do note that the function declaration would have to be changed to accommodate this, eg: Function Inventory(var ComAdr : Byte; var State : Byte; var AFI : Byte; var DSFIDAndUID : TTagBuffer; var CardNum : Byte; frmComPortindex : Integer): LongInt; stdcall; external 'RR3036.dll'; Otherwise, you would have to type-cast the DSFIDAndUID variable when passing it: var DSFIDAndUID : TTagBuffer; Get_UID_RFID_Tag_Data_Status := Inventory(fComAdr, State, AFI, PChar(@DSFIDAndUID), CardNum, frmcomportindex); -
Read of address DEADBEE7. - Source of this raise?
Remy Lebeau replied to Lars Fosdal's topic in General Help
In some piece of code somewhere, a pointer was set to $DEADBEEF, most likely after the object it was pointing to had been freed, and then that same pointer was used afterwards by code running at address $0040CEEC trying to access a data member at offset -8 ($DEADBEE7) via that pointer. Magic addresses like $DEADBEEF are commonly used for debugging such mistakes. In this case, the code at address $0040CEEC is the System._UStrAsg() function, which is used to assign a UnicodeString to another UnicodeString. Offset -8 is the StrRec.refCount field in the UnicodeString data header, which makes sense as UStrAsg() does increment that field. In other words, some piece of code tried to assign a UnicodeString value to a dead UnicodeString variable. UStrAsg() was called by TFieldValue.SetValue(), so SetValue() was likely called on an invalid TFieldValue object inside of TGridSet.CopyRow(), which was called by TDeliverySet.UpdateFromDeliverySet() inside of TDeliveryGrid.RefreshByRouteList(). So, start in RefreshByRouteList() and work your way into UpdateFromDeliverySet() and try to figure out how it may be accessing an invalid TFieldValue object. The Operation System, when the invalid memory address $DEADBEE7 is accessed. Delphi's RTL catches that error from the OS, creates an EAccessViolation object for it, and then raises it. -
Possible bug in debugger for Delphi 10.3.1
Remy Lebeau replied to Sherlock's topic in Delphi IDE and APIs
Because this is NOT a change in the debugger itself! It is a change in the implementation of the class that you are trying to debug. TWO DIFFERENT THINGS. Just because you can't see the data anymore doesn't mean it is "broken". It just means the data is abstracted away and hidden from you. -
Like I said, use the Shell Library API for that. See Windows Libraries and Using Libraries in your Program for more details. You don't need to repeat yourself. You made yourself perfectly clear the first time you said that.
-
When working with Libraries, you should use the IShellLibrary interface to query them. However, in this particular example, the correct way to get the path to the user's Pictures folder is to use SHGetFolderPath(CSIDL_MYPICTURES) or SHGetKnownFolderPath(FOLDERID_Pictures) instead. LShellItem can't be nil if SHCreateItemFromParsingName() returns success.
-
Perhaps. It was so long ago, we don't know what their actual thinking was. Such a change would break existing implementations, forcing rewrites. It was probably the lesser of two evils at the time to introduce the overload to maintain compatibility rather than force people to update their code. Of course, the issue wasn't helped when a 3rd Seek overload was added in XE4, causing ambiguity errors under OSX/iOS 64bit for user code that was never updated to use the original overload added in D6. Speaking from experience, I'm in a similar boat right now, where I have to update a legacy product with a huge codebase to migrate it from using 32bit ints to 64bit ints, and it is going to be a MAJOR undertaking, affecting databases and external APIs, and requiring rewrites of internal logic in various places. Who would have thought 20 years ago that our customer data would ever need to exceed the limits of 10 digit integers, but it has for some time now and we have worked around the problem over the years by truncating data in various ways to maintain compatibility, instead of redesign the system. We finally looked at a redesign awhile ago, but all of our team members have been let go or moved to other teams before it was viable. Now a redesign is being looked at again and I'm the only one left to work on it. So, it is not always an easy task to replace code rather than overload code.
-
It was done this way for backwards compatibility when TStream was first updated to support 64bit streams. Since all implementations and uses of TStream were based on 32bit operations at the time (to this day, some TStream implementations are still 32bit only - ahem, TMemoryStream), when the new 64bit Seek was added, nobody overrode it yet, so it would call the 32bit Seek by default so existing implementations would still work. Once a stream class was updated to override the 64bit Seek instead of the 32bit Seek, the 32bit Seek would then call the 64bit Seek by default, thus still maintaining compatibility with existing code. The 32bit Seek checking if the 64bit Seek is overriden before calling it is just a safety catch to avoid a recursive loop should an implementation make the mistake of not overriding either version of Seek correctly.
-
The Spring4D approach uses roughly the same principle - comparing the base method and derived method to see if they are located at the same memory address or not - but it goes about doing so in a very different and more intrusive manner. It manually looks for the base method address in the base class's vtable to get the method's index within the vtable, and then it retrieves the address stored in the vtable of the derived class at the same index, and then compares the two addresses. The Seek() approach is cleaner in that it lets the compiler deal with the vtable details.
-
To expand on Anders' comment - the TStream.Seek() method has two overloads, one for 32-bit seeks and one for 64-bit seeks. By default, the 32-bit Seek() calls the 64-bit Seek() and vice versa. Descendants must override one of the Seek() methods and the override must not call the inherited method. To avoid an endless recursive loop, the 32-bit Seek() validates that the 64-bit Seek() method has been overridden before calling it. This is the technique that it uses for that validation: procedure TAncestor.Do(); type TMethodXType = procedure of object; // must match the signature of MethodX... var Impl: TMethodXType; Base: TMethodXType; ClassTAncestor: TClass; begin Impl := MethodX; // points to descendant's implementation of MethodX... ClassTAncestor := Self.ClassType; // get object's actual class type... while (ClassTAncestor <> nil) and (ClassTAncestor <> TAncestor) do // find the TAncestor base class... ClassTAncestor := ClassTAncestor.ClassParent; Base := TAncestor(@ClassTAncestor).MethodX; // points to TAncestor's implementation of MethodX... if TMethod(Impl).Code = TMethod(Base).Code then // which MethodX implementation is the object actually using? begin // MethodX is NOT overriden... end else begin // MethodX IS overriden... end; end;
-
Using dxgettext on Windows 10
Remy Lebeau replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
I also find that annoying. If you click on the quote, an X appears in the top-left corner, then click on that and hit backspace or delete, and SOMETIMES that works, but rarely. In fact, I'm not very impressed with how this site behaves in a mobile browser overall, which sucks because I spend more time lately using mobile browsers than desktop browsers when visiting various support forums. If I try to select more than a few words of text at a time, the selection jumps all over the place, which makes quoting and copy/pasting near impossible. And I can't insert code blocks, there is no option for that in the editor toolbar, like there is in a desktop browser. And it took me forever to find the "Mark site read" option in the mobile site, I finally found it in the top-level menu bar, but think it would make more sense for it to be in the "Activity" section next to "Unread content", not in the "Account" section. -
Compiling code originally for VS in C++Builder
Remy Lebeau replied to Dave Nottage's topic in General Help
Works fine for me. The extern "C" has nothing to do with the exporting itself, that is handled by the __declspec. The extern "C" simply disables name mangling on the exported name. I've never had to resort to that. But then again, I've never tried linking to 3rd party static .lib files that are not already compatible with C++Builder, and import .lib files for DLLs are easy to regenerate using C++Builder's implib.exe. -
Using dxgettext on Windows 10
Remy Lebeau replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
I can think of one reason: System.UTF8ToUnicode(), and its companion System.UnicodeToUTF8(), were broken or more accurately incomplete in Delphi 6-2007, as they did not support 4-byte UTF-8 sequences (Unicode codepoints outside of the BMP), only 1-3 byte sequences (Unicode codepoints in the BMP) . That was not fixed until Delphi 2009, when they were rewritten to use platform conversions instead of manual conversions. Now granted, at the time, 4-byte UTF-8 sequences were pretty rare, typically only seen in strings using Eastern Asian languages. But in modern Unicode, they are much more common now, especially with the popularity of emojis on the rise, most of which use high codepoint values outside the BMP. -
GnuGetText.pas using Utf8ToUnicodeString instead of Utf8ToWideString
Remy Lebeau replied to dummzeuch's topic in Tips / Blogs / Tutorials / Videos
UTF8String was first introduced in Delphi 6 (but it did not become a true UTF-8 string until Delphi 2009). Delphi 6 has the following UTF8 <-> UTF16 functions in the System unit: function UnicodeToUtf8(Dest: PChar; Source: PWideChar; MaxBytes: Integer): Integer; overload; deprecated; function Utf8ToUnicode(Dest: PWideChar; Source: PChar; MaxChars: Integer): Integer; overload; deprecated; function UnicodeToUtf8(Dest: PChar; MaxDestBytes: Cardinal; Source: PWideChar; SourceChars: Cardinal): Cardinal; overload; function Utf8ToUnicode(Dest: PWideChar; MaxDestChars: Cardinal; Source: PChar; SourceBytes: Cardinal): Cardinal; overload; function UTF8Encode(const WS: WideString): UTF8String; function UTF8Decode(const S: UTF8String): WideString; Do note, however, that these functions did not support 4-byte UTF-8 sequences (Unicode codepoints outside of the BMP), only 1-3 byte sequences (Unicode codepoints in the BMP). That was not fixed until Delphi 2009, when they were rewritten to use platform conversions instead of manual conversions. Now granted, at the time, 4-byte UTF-8 sequences were pretty rare, typically only seen in strings using Eastern Asian languages. But in modern Unicode, they are much more common now, especially with the popularity of emojis on the rise, most of which use high codepoint values outside the BMP. -
use indy in a console application
Remy Lebeau replied to bernhard_LA's topic in Algorithms, Data Structures and Class Design
Like the events of most other components, the events of Indy components expect class methods, not standalone functions. You could go the way that others have recommended, where creating a TDataModule at design-time and using it at run-time would be the easiest. But, even if you were to simply write a class in code to wrap the events, note that you don't actually need to instantiate such a class at run-time, you can declare its methods with the class directive instead, eg: program IndyConsoleApp; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, IdContext, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, IdGlobal; var IdTCPServer1: TIdTCPServer; procedure ShowStartServerdMessage; begin WriteLn('START SERVER @' + TimeToStr(now)); end; procedure StopStartServerdMessage; begin WriteLn('STOP SERVER @' + TimeToStr(now)); end; type TCPServerEvents = class class procedure OnExecute(AContext: TIdContext); end; class procedure TCPServerEvents.OnExecute(AContext: TIdContext); var LLine: String; begin LLine := AContext.Connection.IOHandler.ReadLn(); writeln(LLine); AContext.Connection.IOHandler.WriteLn('OK'); end; begin try IdTCPServer1 := TIdTCPServer.Create; try with IdTCPServer1.Bindings.Add do begin IP := '127.0.0.1'; Port := 6000; end; IdTCPServer1.OnExecute := TCPServerEvents.OnExecute; IdTCPServer1.Active := True; try ShowStartServerdMessage; Readln; finally IdTCPServer1.Active := False; StopStartServerdMessage; end; finally IdTCPServer1.Free; end; except on E: Exception do WriteLn(E.ClassName, ': ', E.Message); end; end. However, there IS actually a way to use a standalone function instead, but it involves a little trickery using Delphi's TMethod record: program IndyConsoleApp; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, IdContext, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, IdGlobal; var IdTCPServer1: TIdTCPServer; procedure ShowStartServerdMessage; begin WriteLn('START SERVER @' + TimeToStr(now)); end; procedure StopStartServerdMessage; begin WriteLn('STOP SERVER @' + TimeToStr(now)); end; procedure TCPServerExecute(ASelf: Pointer; AContext: TIdContext); // NOTE THE EXTRA PARAMETER! var LLine: String; begin LLine := AContext.Connection.IOHandler.ReadLn(); WriteLn(LLine); AContext.Connection.IOHandler.WriteLn('OK'); end; var ExecuteFct: TMethod; begin try IdTCPServer1 := TIdTCPServer.Create; try with IdTCPServer1.Bindings.Add do begin IP := '127.0.0.1'; Port := 6000; end; ExecuteFct.Data := nil; // or anything you want to pass to the ASelf parameter... ExecuteFct.Code := @TCPServerExecute; IdTCPServer1.OnExecute := TIdServerThreadEvent(ExecuteFct); IdTCPServer1.Active := True; try ShowStartServerdMessage; Readln; finally IdTCPServer1.Active := False; StopStartServerdMessage; end; finally IdTCPServer1.Free; end; except on E: Exception do WriteLn(E.ClassName, ': ', E.Message); end; end. -
Who is doing MacOS app with RadStudio for clients ?
Remy Lebeau replied to Rollo62's topic in Cross-platform
Yes. The OSX 64bit compiler is still in development and is scheduled to be released sometime in a future 10.3.x update. -
best practise sharing : 2 buttons with mrOK on a form
Remy Lebeau replied to bernhard_LA's topic in VCL
There is no way to differentiate which control set the ModalResult if they both set it to the same value. ShowModal() will close the Form when any non-zero value is assigned to ModalResult, you are not limited to the handful of constants that the RTL defines, use whatever values you want. -
Compiling code originally for VS in C++Builder
Remy Lebeau replied to Dave Nottage's topic in General Help
You appear to be mixing C/C++ and Delphi syntaxes. It should be: extern "C" __declspec(dllexport) int Load(PDLLLINK pLink); Or: extern "C" int __declspec(dllexport) Load(PDLLLINK pLink); -
Compiling code originally for VS in C++Builder
Remy Lebeau replied to Dave Nottage's topic in General Help
Those modifiers can be applied directly to the function declaration. But, are you referring to a VS static library that you want to link inside your C++Builder DLL? Or are you referring to the import lib that the C++Builder compiler outputs in addition to the DLL? I'm confused about your setup and what you are trying to accomplish exactly. -
Compiling code originally for VS in C++Builder
Remy Lebeau replied to Dave Nottage's topic in General Help
I rarely use .DEF files in my C++Builder DLLs, I prefer to use 'extern "C"' and __declspec(dllexport) in the DLL's source code instead. .DEF files are good when you need custom control over how exports are name-mangled and/or assigned ordinal values. In most cases, that is not necessary. I have never seen that error before, and the only reference to it I find online is this: https://stackoverflow.com/questions/5707046/ However, I have never upgraded my C++Builder to a version that is based on MSBuild, either. I still use C++Builder 6. -
Compiling code originally for VS in C++Builder
Remy Lebeau replied to Dave Nottage's topic in General Help
bcc32c is the CLang-based Windows 32bit compiler. What you are seeing is a CLang error message. See Stricter C++ Compilers (Clang-enhanced C++ Compilers): Token Pasting in Embarcadero's documentation. Also, I don't think __VA_ARGS__ will work in this situation, as that would pass FuncName as the first parameter to FuncName(). I'm sure Hpc_Trace_AddClassId() does not expect Hpc_Trace_AddClassId as its first parameter value. In the Project Options, in the C++ Compiler section, there is a "Use 'classic' Borland compiler" option. -
Delphi inline and function evaluation
Remy Lebeau replied to Mahdi Safsafi's topic in Tips / Blogs / Tutorials / Videos
Why does that surprise you? ANY compiler worth its salt will perform optimizations, especially when dealing with inline functions, and would be smart enough to recognize when basic arithmetic operations are being performed on compile-time literals and just output code for the result of those operations at compile-time instead of generating code to perform those operations at runtime. "5 > 9" is ALWAYS false, so the compiler can just output False instead of outputting code to compare 5 to 9. And "if False Then" will ALWAYS skip the IF block and jump to the ELSE block. so the compiler can ignore any of the code in the IF block. As such, your call to Max(5, 9) gets optimized to simply "Result := 9" when inlined, and so the compiler can simply replace the entire call to Max(5, 9) with just the literal 9. -
Compiling code originally for VS in C++Builder
Remy Lebeau replied to Dave Nottage's topic in General Help
What kind of errors exactly? Also, which version of C++Builder are you using? Which of its many C++ compilers specifically are you using? Are you using a compiler that is compatible with the C++ version that the VS code is targeting? That would imply that the .lib file is probably a static library instead of an import lib for a DLL. If the VS project is for a static library rather than a DLL, then compiling it as a DLL may not be helpful since its code would likely not be setup to export symbols that IMPLIB could find. -
OverbyteIcscryptuiapi.h what is it for?
Remy Lebeau replied to nSolvePaul's topic in ICS - Internet Component Suite
I think such an HPPEMIT statement should not be needed at all. Delphi .PAS files output .HPP files, not .H files, when compiled for C++Builder, and there is no need for a .PAS file to ever HPPEMIT a reference to its own .HPP file. -
OverbyteIcsJwaWinCrypt.inc missing ';' in the HPPEMIT
Remy Lebeau replied to nSolvePaul's topic in ICS - Internet Component Suite
I can confirm the semicolons are required, in ALL C++ versions and compilers. Typedef statements need to be terminated by a semicolon, just like any other statement in C++.