-
Content Count
2985 -
Joined
-
Last visited
-
Days Won
135
Everything posted by Remy Lebeau
-
That Sockets unit is OLD (dating back to Delphi 6) and is not maintained. That unit was Borland's (before Embarcadero owned Delphi) attempt at providing cross-platform socket classes for Kylix, but it doesn't work very well, and they decided to drop it in favor of Indy in later versions. In fact, that unit is not even distributed with Delphi anymore, XE was the last version to include it. And even if it were still distributed, the 3 class functions you mention never existed in it, not even in 2010, they were always non-static methods of the TIpSocket class, so you could never call them as standalone functions. I'm not quite sure what you are asking for, but you should not be using that Sockets unit in modern projects at all.
-
TEdit is just a wrapper for a standard Win32 edit control, which does not support vertical alignment, only horizontal.
-
If you are using an up-to-date version of Indy, the IdSSLOpenSSLHeaders unit has IdOpenSSLSetCanLoadSymLinks() and IdOpenSSLSetLoadSymLinksFirst() functions available. By default, Indy attempts to load unversioned dylibs before loading versioned dylibs. You can turn off this behavior, which is useful in cases where the unversioned dylibs are symlinks to versioned dylibs that are not compatible with Indy. Or, in this case, to turn off the loading of unversioned dylibs altogether.
-
Code has already been written to do exactly that, actually, but it hasn't been checked in yet.
-
Why is ShowMesssage blocking all visible forms?
Remy Lebeau replied to Mike Torrettinni's topic in VCL
Then don't use ShowMessage() for that. It displays a modal TForm whose owner window (in Win32 API terms, not VCL terms) is the currently active TForm. An owned window is always on top of its owning window. For what you describe, use a normal TForm instead of ShowMessage(). Set its PopupParent property to the monitoring TForm that needs attention, and then show the popup TForm. This way, the popup Tform stays on top of only its monitoring TForm, but other TForms can appear on top of the popup TForm. Otherwise, change your UI to not use a popup TForm at all. Put a visible message inside the TForm that needs attention. -
The TDictionary.Keys property uses a singleton, so no matter how many times you read the Keys property itself, there will only be 1 TKeyCollection object in memory. However, every time you call ToArray() on that collection, it will allocate a new array in memory and populate it with the current keys. Dynamic arrays are reference counted, so every assignment to the local tdictkeys variable will decrement the refcount of the old array, freeing it, and increment the refcount of the new array. When the method exits, the refcount of the last array allocated is decremented, freeing it. If the dictionary is continuously growing in count, then the loop above will allocate larger and larger arrays accordingly, , where a new array is allocated before the previous array is freed. So yes, that has the potential to cause an OOM error over time, depending on just how large the dictionary actually grows. At the assembly level, yes. But if the binary has external dependencies on DLLs/APIs that don't exist on the machine (and modern versions of Delphi's RTL do use APIs that don't exist in Win2K), then the binary will not be loaded by the OS and will not be allowed to run any of its code.
-
How to iterate a TDictionary using RTTI and TValue
Remy Lebeau replied to John Kouraklis's topic in Delphi IDE and APIs
In that TPair record, TKey and TValue are Generic parameters, they belong to the TPair type itself. In C++, that TPair record type gets renamed to TPair__2 because of the Generic parameters. When the Delphi compiler generates a C++ .hpp file, it will declare a constructor inside that type. But since the type is renamed for C++, the HPPEMITs are being used to provide an implementation for that constructor using the new name. I have never seen the [HPPGEN] attribute before, though. It is not documented. -
tidtcpserver read (peek) without removing from buffer.
Remy Lebeau replied to alnickels's topic in Network, Cloud and Web
(copied from my answer to your same question on StackOverflow😞 Indy is not really designed for peeking data, it would rather that you read whole data, letting it block until the requested data has arrived in full. That being said, TIdBuffer does have a PeekByte() method: function PeekByte(AIndex: Integer): Byte; var B: Byte; if AContext.Connection.IOHandler.InputBuffer.Size > 0 then begin B := AContext.Connection.IOHandler.InputBuffer.PeekByte(0); ... end; Or, if you are looking for something in particular in the buffer (ie, a message delimiter, etc), TIdBuffer has several overloaded IndexOf() methods: function IndexOf(const AByte: Byte; AStartPos: Integer = 0): Integer; overload; function IndexOf(const ABytes: TIdBytes; AStartPos: Integer = 0): Integer; overload; function IndexOf(const AString: string; AStartPos: Integer = 0; AByteEncoding: IIdTextEncoding = nil {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} ): Integer; overload; var Index: Integer; Index := AContext.Connection.IOHandler.InputBuffer.IndexOf(SingleByte); Index := AContext.Connection.IOHandler.InputBuffer.IndexOf(ArrayOfBytes); Index := AContext.Connection.IOHandler.InputBuffer.IndexOf('string'); ... -
No, there is not.
-
You can hexdump each compiler EXE to see which conditional symbols are actually available. That is what I do sometimes.
-
How to iterate a TDictionary using RTTI and TValue
Remy Lebeau replied to John Kouraklis's topic in Delphi IDE and APIs
Yes, if you can't specify the actual TKey/TValue Generic parameters up front at compile-time when defining how you access the dictionary object from the Sytem.Rtti.TValue record. In which case, you can't use a standard "for..in" loop to enumerate the dictionary. All the necessary type information is lost once you access the dictionary object via the System.Rtti.TValue record if you can't type-cast the object. So yes, this is the only way I can think to enumerate the dictionary object using only RTTI and not caring what the actual Generic parameters are. I mean, if you really needed the RTTI for those types, you could take the dictionary object's ClassName() string, parse the typenames between the angle brackets, and resolve them using TRttiContext.FindType(), but that won't help you to enumerate the dictionary at all since you still need access to its enumerators (either via its GetEnumerator() method or its Keys+Values properties). I really couldn't say. Probably not, if you use the RTTI correctly and handle the enumeration correctly. Hopefully the RTTI and Sytem.Rtti.TValue record will handle the complexities for you. It doesn't, outside of the implementation code for the TDictionary class, where the TKey and TValue Generic parameters have meaning. Outside of the class, they don't exist. They are part of the class type itself, for instance TDictionary<string, int> and TDictionary<int, char> are separate and distinct types with no relation to each other. No. {$HPPEMIT} has no effect whatsoever on how the Delphi compiler processes code. {$HPPEMIT} merely outputs an arbitrary line of text to a C++ .hpp header file, if one is being generated while the Delphi compiler is parsing the code. Curious - what version of Delphi are you using? I have RTL sources up to XE3, and there are no {$HPPEMIT} statements at all in System.Generics.Collections.pas in those versions, so it must be something added in more recent versions. -
How to iterate a TDictionary using RTTI and TValue
Remy Lebeau replied to John Kouraklis's topic in Delphi IDE and APIs
Generics are not easy to serialize manually because of the nature of their dynamic types. You will likely have to resort to something like the following (untested, but should give you an idea of what is involved): var mc: TMClass; ctx: TRttiContext; rType: TRttiType; rProp: TRttiProperty; rMethod: TRttiMethod; rKeyField, rValueField: TRttiField; propValue, methValue, genKey, genValue: TValue; genDict, genDictEnum: TObject; genPair: Pointer; begin mc := TMClass.Create; try { if Assigned(mc.MC) then begin for var pair in mc.MC do begin // use pair.Key and pair.Value as needed ... end; end; which is actually this behind the scenes ... if Assigned(mc.MC) then begin var enum = mc.MC.GetEnumerator; while enum.MoveNext do begin var pair = enum.Current; // use pair.Key and pair.Value as needed ... end; end; } rType := ctx.GetType(mc.ClassInfo); // rType = TMClass rProp := rType.GetProperty('MC'); propValue := rProp.GetValue(mc); genDict := propValue.AsObject; if Assigned(genDict) then begin rType := rProp.PropertyType; // rType = TObjDict rMethod := rType.GetMethod('GetEnumerator'); methValue := rMethod.Invoke(genDict, []); genDictEnum := methValue.AsObject; rType := rMethod.ReturnType; // rType = TDictionary<TKey, TValue>.TPairEnumerator rMethod := rType.GetMethod('MoveNext'); rProp := rType.GetProperty('Current'); rType := rProp.PropertyType; // rType = TPair<TKey, TValue> rKeyField := rType.GetField('Key'); rValueField := rType.GetField('Value'); methValue := rMethod.Invoke(genDictEnum, []); while methValue.AsBoolean do begin propValue := rProp.GetValue(genDictEnum); genPair := propValue.GetReferenceToRawData; genKey := rKeyField.GetValue(genPair); // genKey.TypeInfo = TypeInfo(string) genValue := rValueField.GetValue(genPair); // genValue.TypeInfo = TypeInfo(string) // use genKey and genValue as needed ... methValue := rMethod.Invoke(genDictEnum, []); end; end; finally mc.Free; end; end. Because it doesn't exist. For a dictionary, TKey and TValue are Generic parameters of the TDictionary class. They are not standalone concrete types, like you are thinking of. -
How to iterate a TDictionary using RTTI and TValue
Remy Lebeau replied to John Kouraklis's topic in Delphi IDE and APIs
You have a mismatch in your code: Do you see it? String <> TValue. You need to fix your declaration of the genDict variable to match the TObjDic type. In fact, why are you not using TObjDict itself in the declaration of genDict? type TObjDic = TObjectDictionary<string, string>; ... var ... genDict: TObjDic; begin ... genDict := propvalue.AsObject as TObjDic; ... -
How to iterate a TDictionary using RTTI and TValue
Remy Lebeau replied to John Kouraklis's topic in Delphi IDE and APIs
Then please show your actual code. It sounds like you are trying to instantiate TKey objects, which you should not be doing at all. You don't need to create a TKey object in order to enumerate a TDictionary. Use the TDictionary.Keys and TDictionary.Values properties, or the TDictionary.GetEnumerator() method. -
How to iterate a TDictionary using RTTI and TValue
Remy Lebeau replied to John Kouraklis's topic in Delphi IDE and APIs
I imagine trying to access the content of a TDictionary via RTTI will be very difficult, if not down-right dangerous due to the extra work needed to deal with managed types and such. You would be better off simply retrieving the TDictionary object pointer from the property (ie, using "propvalue.AsType<TDictionary<TKey, TValue>>" or "propvalue.AsObject as TDictionary<TKey, TValue>") and then access its content via normal TDictionary methods, not via RTTI at all. -
Unlike in 32bit, multiple calling conventions simply do not exist in 64bit. There is only 1 calling convention, and it is dictated by the 64bit ABI itself. Keywords like 'cdecl' and 'stdcall' are ignored when compiling for 64bit, so as not to break code that is being ported from 32bit.
-
Is Class with 2 'nested' constructors bad design?
Remy Lebeau replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
I hope your class never gets used in C++, because the resulting constructors would have the exact same names and parameter lists, and thus be ambiguous and unusable! -
Address and port are already in use error after TIdHttp.Get
Remy Lebeau replied to eivindbakkestuen's topic in Indy
Are the TIdHTTP.BoundIP and TIdHTTP.BoundPort(Min|Max) properties set to '' and 0, respectively? When the error happens, what is the actual value of the TIdHTTP.Socket.Binding.IP, TIdHTTP.Socket.Binding.Port, and TIdHTTP.Socket.Binding.ClientPort(Min|Max) properties? They should also be '' and 0, respectively. If they are not, then something weird is going on. By default, TIdHTTP should be binding to local port 0, allowing it to use a random local ephemeral port assigned by the OS. There should be no "address and port already in use" error for just 10 connections if they are all on random local ports. Which implies that there may be a non-zero port being used explicitly somewhere. -
Address and port are already in use error after TIdHttp.Get
Remy Lebeau replied to eivindbakkestuen's topic in Indy
Are you reusing the same TIdHTTP object for multiple Get() calls? Unless you are explicitly setting the TIdHTTP.BoundPort(Min|Max) properties, I don't see any other way you can get that error, unless you are creating so many connections that you are just exhausting the OS's available local ports over time. Possibly. Hard to say without seeing your actual code, or at least knowing how many threads you are using. -
Image pointer and buffer size
Remy Lebeau replied to dkprojektai's topic in Algorithms, Data Structures and Class Design
Without knowing exactly what that function actually is or where it comes from, and given how few parameters it takes, it MOST LIKELY is expecting the WHOLE bitmap, header and everything, not just the raw pixel data by itself. If so, you would just have to load your BMP file or TBitmap object into a TMemoryStream first, and then you can pass its Memory and Size properties to that function's parameters, eg: MS := TMemoryStream.Create; try MS.LoadFromFile('image.bmp'); or BmpObj.SaveToStream(MS); Result := ReadFromMemFile(hEngine, MS.Memory, MS.Size); finally MS.Free; end; -
Image pointer and buffer size
Remy Lebeau replied to dkprojektai's topic in Algorithms, Data Structures and Class Design
The TBitmap.ScanLine property getter handles top-down vs bottom-up differences for you. You can use scanline indexes as if the bitmap were always top-down, like a normal array, and the property getter will access the raw pixels as needed based on whether the bitmap is top-down or bottom-up. In other words, ScanLine[0] always represents the 1st line of pixels, ScanLine[1] always represents the 2nd line of pixels, and so on. function TBitmap.GetScanLine(Row: Integer): Pointer; begin Changing(Self); with FImage.FDIB, dsbm, dsbmih do begin if (Row < 0) or (Row >= bmHeight) then InvalidOperation(@SScanLine); DIBNeeded; GDIFlush; if biHeight > 0 then // bottom-up DIB Row := biHeight - Row - 1; Result := PByte(bmBits) + Row * BytesPerScanline(biWidth, biBitCount, 32); end; end; That means if you want a raw pointer to the entire image buffer as a whole, then you have to use ScanLine[0] for a top-down bitmap, and ScanLine[Height-1] for a bottom-up bitmap. But, if you just want to access the individual lines one at a time, then you can loop through ScanLine[0]..ScanLine[Height-1] without regard to whether the bitmap is top-down or bottom-up. -
When that error happens, you can then call Indy's WhichFailedToLoad() function in the IdSSLOpenSSLHeaders unit to find out WHY it failed. Either the DLLs themselves could not be loaded into memory, or else they are missing exports that Indy requires. Indy supports up to the latest OpenSSL 1.0.2 DLLs. Those DLLs are known to be working with Indy.
-
I think that was the main reason why we switched from InterBase to FireBird many years ago.
-
As long as they are using separate IP/Ports, and any NAT proxy/router they are sitting behind has been configured properly for port forwarding, then there should be no problem. No.
-
Internationalized Domain Names (IDN)
Remy Lebeau replied to Angus Robertson's topic in ICS - Internet Component Suite
No. But that does not mean IDN is not important to support, though. Granted, the majority of the Internet does not use IDNs, but some parts do, and you never know what you users are going to want to access.