Jump to content

Remy Lebeau

Members
  • Content Count

    2914
  • Joined

  • Last visited

  • Days Won

    130

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Compilng in x64 gives a warning I see why occurs

    In C++11 onward, a string literal (in your case, each string literal is a 'const wchar[N]' array) cannot be assigned to a pointer-to-non-const, as that promotes the ability to modify read-only data at runtime. In C, and prior to C++11, such an assignment was allowed. But BCC64 is a clang-based compiler that supports C++11, hence the warning (which is really an error nowadays). This is actually documented (albeit briefly) on Embarcadero's DocWiki: Common Warnings Porting from BCC32: Conversion from string literal to 'char *' is deprecated Stricter C++ Compilers: Type Conversions Yes, but don't do that. The correct solution is to make your array hold pointer-to-const elements instead (which you should have done from the very beginning), eg: const wchar_t *ConfigurationResultText[]={ // or wchar_t const * L"No error", L"CFGERROR: Unable to create file", L"CFGERROR: Unable to open file or file is busy", L"CFGERROR: Unable to perform file IO", L"CFGERROR: File is not a CFG file", }; Because it is required in C++11 onward. Assigning a string literal (which decays into a pointer-to-const) to a pointer-to-non-const was bad practice in earlier versions, so the C++ committee finally closed that hole. It is a 'const wchar[N]' array that decays into a 'const wchar_t*' pointer, not a 'wchar_t*' pointer. The classic compiler is not a clang-based compiler, and does not support C++11 (but has some C++11 features, but this isn't one of them). But adding the 'const' to the array element type will work in the classic compiler, too. If you run into a problem where you need to pass your string literal pointers to a legacy API that takes a pointer-to-non-const rather than a pointer-to-const, then you can type-cast (preferably using 'const_cast', not a C-style cast) at the call site, not in your array itself, eg: const wchar_t *ConfigurationResultText[]={ // or wchar_t const * L"No error", ... }; ... someLegacyFunction(const_cast<wchar_t*>(ConfigurationResultText[index]));
  2. Remy Lebeau

    Another case of tlsv1 alert protocol version

    Only if you want to be able to use Indy components in a Form Designer at design-time. Otherwise, you can just create the components dynamically in code at runtime. Yes. Though, the runtime package binaries do need to be located where the IDE can find them when loading the design-time package binaries. Since you already compiled the binaries, you don't really need to compile them again, so you don't need to open the projects. The "Install Packages" dialog is just looking for the .BPL binaries. But yes, it would just be the two DCL packages. None. You already created them. Basically, you can't. But you can re-enable it when needed. No, you can't. But you can install packages on a per-project basis. So, make sure no project is loaded, and then go into the "Install Packages" dialog and remove the Indy packages. Then, load a project and re-add the appropriate Indy packages you want to use for that project. Then close the project, load another project, and repeat. As long as you maintain the separate Indy versions, you should be able to pick which version you want to use for each project. Of course, this is all just contingent on installing Indy in the IDE at all. If you create Indy components dynamically in code instead, then this matter becomes moot, since you can just make each project reference the appropriate Indy source/binaries folder as needed. Yes.
  3. Remy Lebeau

    Parse Json

    Don't forget to call JsonValue.Free() when you are done using JsonValue, or else it will be leaked. This is a common use-case for a try..finally block.
  4. Remy Lebeau

    Delphi Community Edition

    No, Community Edition does not include Konopka. The Feature Matrix even say so:
  5. Remy Lebeau

    Avoiding use of BPLs

    Design-time code (property editors, component editors, IDE wizards, etc) are simply not allowed in runtime executables, period. So, your home-grown component needs to be implemented in a runtime-only package, and any code that relies on the DesignIDE package needs to be implemented in a designtime-only package that "requires" the runtime-only package. Your component should have no concept of design-time whatsoever (outside of things like the csDesigning flag in its ComponentState property, the CM_DESIGNHITTEST message, etc). If that is not the case, then the component is not architectured properly.
  6. Remy Lebeau

    Another case of tlsv1 alert protocol version

    Sounds about right. Modern Delphi prefers .dproj files for storing Project settings. When you open a .dpk file and no .dproj file exists, a new .dproj file is created from the settings in the .dpk file. And since you are not using a .groupproj file for a Project Group, a default Project Group is created for you, as well. Yes. And then repeat that for all 5 Indy packages (in this order): IndySystem, IndyCore, IndyProtocols, dclIndyCore, dclIndyProtocols. And then you can install the 2 dcl Packages in the IDE if you want to (replacing the old one), or just update individual projects to reference the newer non-dcl packages as needed. That, I can't answer for sure. It varies from one IDE version to another. I don't know what the default are for XE. You can go into the IDE settings to see exactly where the BDSCOMMONDIR environment variable maps to. That would probably be a good idea. If you go into the IDE's Packages list, you can see exact where it loads installed BPLs from.
  7. Remy Lebeau

    Another case of tlsv1 alert protocol version

    There is no .BAT script for Delphi XE, only for C++Builder XE. Indy stopped using .BAT scripts for Delphi installations after D2009. Instead, you can simply open the 5 .DPK packages directly in the IDE (sadly, there is no Project Group file provided for XE) and then compile+install them from the Project Manager. You would have to maintain separate copies of Indy in different folders, and then configure individual projects to use one folder or the other when referencing the Indy packages/sources.
  8. Is ICS using the SO_REUSEADDR/SO_REUSEPORT socket options? If not, then it should not be possible at all. It is possible to have multiple TCP sockets listening on the same port at the same time ONLY if they are listening on different IPs. Two sockets cannot listen on the same IP/port pair at the same time.
  9. Remy Lebeau

    Another case of tlsv1 alert protocol version

    That is exactly what you would need to do. 10.1.1 is an EXTREMELY OLD version of Indy. The current version is 10.6.2. Be sure to read the install instructions first. Yes, you need those DLLs no matter what. But you would still need an up-to-date Indy to take advantage of those updated DLLs and configure them properly. It is very common for apps to use their own copies of OpenSSL DLLs, to avoid versioning conflicts, as the OpenSSL API tends to change from one major version to another. It should not, since that folder should not be on the system search path, but even if it were, the DLLs should normally be looked for in the app's folder first. But, Indy does have an IdOpenSSLSetLibPath() function in the IdSSLOpenSSLHeaders unit if you need to specify exactly which folder Indy looks in for the DLLs. The revision notes at the top of each Indy .pas file are EXTREMELY OLD, so just ignore them. They are a remnant from when Indy used TeamCoherence for its version control many years ago, before switching to SVN and now GitHub (neither of which store commit details directly in source files themselves). Then you have an EXTREMELY OLD version of Indy installed and need to upgrade. Not without upgrading, no. See my comment above.
  10. Do NOT put non-system files in the System32 folder, they don't belong there. You can install your service from your own folder just fine. On a 64bit Windows, the System32 folder is for 64bit files only. 32bit files would have to go in the SyWOW64 folder instead. But again, don't put your service app in either folder to begin with.
  11. LibreSSL is backwards compatible with OpenSSL 1.0.1, so Indy's OpenSSL loading code has *very limited* support for detecting LibreSSL at runtime and not failing on it. But that is no guarantee that it will actually work correctly for all operations, so use at your own risk. Otherwise, you should install the actual OpenSSL dylibs directly in your app folder instead.
  12. Then it acts more like C++'s std::shared_ptr than std::unique_ptr.
  13. You are actually trying to use OpenSSL 1.0.0, not 1.0.2. 1.0.0 is quite old. That means the code is trying to load the dylibs using unversioned symlinks, which can map to versioned dylibs which you are not expecting, such as for OpenSSL 1.1.x, etc. Don't use symlinks when you need to load a specific version. Out of curiosity, are you using Indy to send your emails? If so, then on 'Nix platforms it has IdOpenSSLSetCanLoadSymLinks() and IdOpenSSLSetLoadSymLinksFirst() functions in the IdSSLOpenSSLHeaders unit. The defaults are both True, but you should set them to False in this situation.
  14. Remy Lebeau

    New to JSON

    The documentStatus field is an array of OBJECTS, not an array of ARRAYS. Look at the JSON again more carefully, each element of the array is wrapped in curly braces (object), not square brackets (array). As such, when iterating the array, the JsonValueArr variable will point at each element of the array, so you MUST type-cast it to TJSONObject, NOT to TJSONArray. Otherwise your code will have undefined behavior due to an incorrect type-cast. If you were to change the type-cast to use 'as' instead, you would get a runtime error. You should not need to type-cast LItem as TJSONPair if you were to declare it as TJSONPair to begin with, rather than TJSONValue. Enumerating a TJSONObject in a for..in loop yields TJSONPair elements. Try this instead: var jasonArr: TJSONArray; JsonValueArr : TJSONValue; LItem: TJSONPair; partialAmount: Double; begin ... jasonArr := GetValue(JsonObject, 'documentStatus') as TJSONArray; for JsonValueArr in jasonArr do begin for LItem in TJSONObject(JsonValueArr){JsonValueArr as TJSONObject} do begin if LItem.JsonString.Value = 'statusCode' then odgovorFinaStatus.b2gStatus := LItem.JsonValue.Value else if LItem.JsonString.Value = 'statusText' then odgovorFinaStatus.b2gStatusTekst := LItem.JsonValue.Value else if LItem.JsonString.Value = 'statusTimestamp' then odgovorFinaStatus.b2gStatusVrijeme := LItem.JsonValue.Value else if LItem.JsonString.Value = 'note' then odgovorFinaStatus.b2gStatusNapomena := LItem.JsonValue.Value else if LItem.JsonString.Value = 'partialAmount' then begin if not TryStrToFloat(LItem.JsonValue.Value, partialAmount) then partialAmount := 0.0; odgovorFinaStatus.b2gDjelIzn := partialAmount; end; ...
  15. Remy Lebeau

    New to JSON

    JsonValueArr is pointing at a TJSONObject, not a TJSONArray, hence why you need to fix that typecast. Enumerating a TJSONObject in a for..in loop will give you TJSONPair elements. What do you have LItem declared as, and why are you type-casting it to TJSONPair inside the loop? JSON object- how to iterate through all properties without knowing their names?
  16. The non-generic TArray is a type in the System unit, and the generic TArray<T> is a type in the System.Generics.Collections unit. The uses clause is used to specify units only, not types. TArray is not its own unit, so don't specify TArray in your uses clause, eg you need to change 'System.Generics.Collections.TArray' to just 'System.Generics.Collections' in the uses clause. But don't change 'System.TArray' to 'System', just drop it entirely, as specify 'System' by itself in a uses clause will cause a compiler error (the System unit is already used implicitly in every unit).
  17. Remy Lebeau

    New to JSON

    Almost. The documentStatus element is an array of objects, not an array of arrays, so you need to cast JsonValueArr to TJSONObject, not to TJSONArray: jasonArr := GetValue(JsonObject, 'documentStatus') as TJSONArray; for JsonValueArr in jasonArr do begin for LItem in TJSONObject(JsonValueArr) do begin ShowMessage(TJSONPair(LItem).JsonString.Value + '/' + TJSONPair(LItem).JsonValue.Value); end; end;
  18. Remy Lebeau

    New to JSON

    Using Delphi's stock JSON library, you already know how to access a named field of a TJSONObject. So, you can simply retrieve the TJSONObject for "b2GOutgoingInvoiceStatus", then retrieve the TJSONValue for its "documentStatus" field, then type-cast that to TJSONArray, and then iterate through its Items[] property, casting each TJSONValue in the array to TJSONObject. Using the JsonDataObjects library instead, you would have to do all of that using whatever its equivalent operations are.
  19. Remy Lebeau

    How to iterate a TDictionary using RTTI and TValue

    Asked and answered on StackOverflow: Do I need to free the enumerator returned by GetEnumerator?
  20. Remy Lebeau

    New to JSON

    ParseJSONValue() is a 'class' function, so you don't need to instantiate an object to call it: JsonValue := TJSonObject.ParseJSONValue(memoRequest.Lines.Text); But more importantly: You are responsible for Free'ing the TJSONValue that ParseJSONValue() returns, so your example is creating a memory leak when it re-assigns the JsonValue variable. Try something more like this instead: var JsonValue: TJSONValue; JsonObject: TJSONObject; invoice: Integer; function GetValue(JsonObject: TJSONObject; const PairName: string): TJSONValue; var JsonPair: TJSONPair; begin JsonPair := JsonObject.Get(PairName); if JsonPair = nil then raise Exception.Create('Pair "' + PairName + '" not found'); Result := JsonPair.JsonValue; end; function GetObject(JsonObject: TJSONObject; const PairName: string): TJSONObject; begin Result := GetValue(JsonObject, PairName) as TJSONObject; end; begin ... invoice := 0; JsonValue := TJSONObject.ParseJSONValue(memoRequest.Lines.Text); if JsonValue = nil then begin // error handling ... end else try try JsonObject := JsonValue as TJSONObject; JsonObject := GetObject(JsonObject, 'b2GOutgoingInvoiceEnvelope'); JsonObject := GetObject(JsonObject, 'b2GOutgoingInvoiceProcessing'); JsonObject := GetObject(JsonObject, 'correctB2GOutgoingInvoice'); invoice := (GetValue(JsonObject, 'invoiceID') as TJSONNumber).AsInt; except // error handling ... end; finally JsonValue.Free; end; // use invoice as needed ... end; If you ever upgrade to XE6 or later, you can use the TJSONObject.FindValue() method instead: type TJSONObjectAccess = class(TJSONObject) end; var JsonValue, InvoiceJsonValue: TJSONValue; JsonObject: TJSONObject; invoice: Integer; begin ... invoice := 0; JsonValue := TJSONObject.ParseJSONValue(memoRequest.Lines.Text); if JsonValue = nil then begin // error handling ... end else try try JsonObject := JsonValue as TJSONObject; InvoiceJsonValue := TJSONObjectAccess(JsonObject).FindValue('$.b2GOutgoingInvoiceEnvelope.b2GOutgoingInvoiceProcessing.correctB2GOutgoingInvoice.invoiceID'); if InvoiceJsonValue = nil then raise Exception.Create('invoiceID not found'); invoice := (InvoiceJsonValue as TJSONNumber).AsInt; except // error handling ... end; finally JsonValue.Free; end; // use invoice as needed ... end;
  21. Why not simply use GetForegroundWindow() or even GetGUIThreadInfo() instead?
  22. Remy Lebeau

    Exception classes implementing interfaces

    You have to use Exception.RaiseOuterException() to capture an InnerException, so it would look more like this: except on E: Exception do begin Exception.RaiseOuterException(EExtended.Create(AddInfo...)); end; end
  23. Remy Lebeau

    Indy OnWork

    You are ignoring the OnWorkBegin and OnWorkEnd events. In particular, the OnWorkBegin event will tell you the actual total number of bytes that are about to be sent. You should be using that value to calculate your progress in the OnWork event, not limiting your progress to just the file size, which will obviously be smaller than the actual transmission. You are correct that there is going to be some extra overhead due to the formatting of the HTTP request, and your progress calculation is not reflecting those extra bytes. Also, you are ignoring the AWorkMode parameter of the events, which will tell you whether the event are being triggered when sending the request to the server or when reading the response from the server. You don't want the latter confusing your progress calculations. Keep in mind that sockets are buffered by default. When TIdHTTP is "sending" data to the server, it is really just putting the data into a kernel buffer. The actual data transmission happens in the background as the kernel sees fit to do so, and there is no way to track the actual transmissions (without hooking into the network hardware, anyway). So, what you are really reporting progress for is the rate in which you are able to queue data for transmission, not the actual transmission itself. If the buffer fills up, because the other party hasn't acknowledged transmitted data yet (ie, you are "sending" faster than the peer is reading), then queuing of new data will block until buffer space clears up as older data gets acknowledged. For most apps, this is adequate. But, if needed, you can try increasing the size of the kernel buffer (Indy doesn't have a property for that, but you can use the TIdHTTP.Socket.Binding.SetSockOpt() method to assign a value to the socket's SO_SNDBUF option manually), or you can try disabling some of the kernel's buffering by setting the TIdHTTP.UseNagle property to false.
  24. Remy Lebeau

    Exception classes implementing interfaces

    I've never seen anyone do it, or even suggest it. But, I don't see any reason why it couldn't be done.
  25. Remy Lebeau

    Pos, SplitString

    What about this? uses ..., StrUtils; if StartsText('SELECT ', TrimLeft(Memo1.Text)) then MyQuery.Open else MyQuery.ExecSql; Or: if Memo1.Text.TrimLeft.StartsWith('SELECT ', True) then MyQuery.Open else MyQuery.ExecSql;
×