Jump to content

egnew

Members
  • Content Count

    29
  • Joined

  • Last visited

Everything posted by egnew

  1. I am converting all my internet support code to use native TNetHttpClient and TNetHttpRequest components. I am getting the exception "No mapping for the Unicode character exists in the target multi-byte code page" for some web pages I am downloading using the function shown below. Here is an instance where the exception occur: GetText('https://www.google.com/index.html'); I assume this is an encoding issue. What is the best way to handle this to get my string result? function TIndigoHttp.GetText (const p_Url: String): String; var v_Response: IHTTPResponse; begin f_Error := ''; // Used by OnRequestError try v_Response := f_NetHTTPRequest.Get(p_Url); f_StatusCode := v_Response.StatusCode; f_StatusText := v_Response.StatusText; Result := v_Response.ContentAsString; except on E: Exception do with v_Response do begin begin Result := ''; f_StatusCode := -1*v_Response.StatusCode; f_StatusText := E.Message+' ['+v_Response.StatusText+']'; end; end; end; end;
  2. Thanks -- I copied your code into my program and it worked. I traced the problem to the constructor of my TIndigoHttp type. I had recently added custom headers to match those chrome was sending when I was having an issue logging into a website. After resolving the logon issue, I forgot to remove the custom headers. The "No mapping for the Unicode character exists in the target multi-byte code page" error occurs when the two custom headers shown below are added to the request.. The error does not occur when I comment out either Add. I am not sure why there is a conflict. Google's encoding is "br". If you want to observe the issue, copy the custom header code below to immediately after you create lhttp. Comment out either Add and the mapping error will not occur. Do you have an idea why the headers cause the error? Thanks for your help, Sidney lhttp.CustHeaders.Clear; lHttp.CustHeaders.Add('Accept-Encoding','gzip, deflate, br, zstd'); lHttp.CustHeaders.Add('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36');
  3. The status is 200 - OK as there is not a problem fetching the webpage. The exception occurs during the call to ContentAsString when TEncoding.GetString is executed. function TEncoding.GetString(const Bytes: TBytes; ByteIndex, ByteCount: Integer): string; var Len: Integer; begin if (Length(Bytes) = 0) and (ByteCount <> 0) then raise EEncodingError.CreateRes(@SInvalidSourceArray); if ByteIndex < 0 then raise EEncodingError.CreateResFmt(@SByteIndexOutOfBounds, [ByteIndex]); if ByteCount < 0 then raise EEncodingError.CreateResFmt(@SInvalidCharCount, [ByteCount]); if (Length(Bytes) - ByteIndex) < ByteCount then raise EEncodingError.CreateResFmt(@SInvalidCharCount, [ByteCount]); Len := GetCharCount(Bytes, ByteIndex, ByteCount); if (ByteCount > 0) and (Len = 0) then raise EEncodingError.CreateRes(@SNoMappingForUnicodeCharacter); SetLength(Result, Len); GetChars(@Bytes[ByteIndex], ByteCount, PChar(Result), Len); end; The value for LEN is zero which causes the EEncodingError exception. As originally stated, I suspect the problem is related to encoding. The question is how to resolve the issue with native Http. I have no problem using Indy as it seems to handle the necessary details on its own. Thanks, Sidney
  4. Hello: ' I have been using Indy to download PDF files containing images of data collected by two of our third party providers. It works fine with Indy but i am having a problem getting past the logon page when using TNetHttpClient and TNetHttpRequest. The logon page loads corrected with a GET and the HTML is correct. I then do a PUT with the username, password, and another required parameter. But when I try to download a file, the download is the content of the logon page, not the file I requested. What do I need to do to download from a website requiring a username and password with specific form requirements for obtaining a session? Thanks
  5. I solved the problem by using Fiddler and found I was missing some parameters needed for the post. I added code to scan the HTML to make sure all required parameters were added to the post. Thanks for your suggestions.
  6. Also, I tried setting CustHeaders in the NetHttpClient. I can also try that in the NetHTTPRequest but it didn't make a difference in the client. with f_NetHttpClient.CustHeaders do begin Clear; Add('Accept','*/*'); Add('AcceptEncoding','gzip,deflate,br,zstd'); Add('AcceptLanguage','en-US,en;q=0.9'); Add('ContentType','application/x-www-form-urlencoded; charset=UTF-8'); add('UserAgent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537..36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'); end; with f_NetHttpClient.CustHeaders do begin Clear; Add('Accept','*/*'); Add('AcceptEncoding','gzip,deflate,br,zstd'); Add('AcceptLanguage','en-US,en;q=0.9'); Add('ContentType','application/x-www-form-urlencoded; charset=UTF-8'); add('UserAgent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537..36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'); end; The documentation only listed a few parameters as available for custom headers but there were many more headers in the chrome debugger. Do limitations exist on what can be placed in the custom headers? Also, the documentation mentions setting MethodString in the request and using the Execute method. That didn't make sense to me when I needed to provide parameters to the POST. Do you think that could be part of the problem?
  7. I have a working Indy version but I want to move everything over to the native components. It is no problem for me to install a sniffer. Am I using the correct steps in my procedures? Thanks, Sidney
  8. I accidentally typed PUT as I am actually using POST. I have not tried a sniffer yet as I have not installed it on my new computer. I do have the information from accessing the site using Chrome's debugger and everything I am doing looks good., I will install the debugger if I can't get it working. Yes I have AllowCookies set to true. I have a unit which defines type TisNetHttp to handle all the details. Pertinent sections of code are shown below. In my main application I set a StringList named v_Params with the required values for the form I accessed using a GET. I then call the routines and check the downloaded file. myHttp.GetText(v_LogonUrl); // Fetches logon page myHttp.Post(v_LogonUrl,v_Params); // Send the form data myHttp.GetFile(v_PDFUrl,v_FileName); // Save the PDF // TYPE TisNetHttp = Class private { Private declarations } f_NetHTTPClient: TNetHTTPClient; f_NetHTTPRequest: TNetHTTPRequest; ... // CONSTRUCTOR f_NetHttpClient := TNetHttpClient.Create(nil); f_NetHttpClient.AllowCookies := True; f_NetHttpClient.HandleRedirects := True; f_NetHttpClient.OnAuthEvent := NetHttpClientAuthEvent; f_NetHttpRequest := TNetHttpRequest.Create(nil); f_NetHttpRequest.Client := f_NetHttpClient; f_NetHttpRequest.OnRequestCompleted := OnRequestCompleted; f_NetHttpRequest.OnRequestError := OnRequestError; procedure TisNetHttp.GetFile (const p_Url, p_FileName: String); // Similar to GetText below except content is saved to file ... function TisNetHttp.GetText (const p_Url: String): String; var v_Response: IHTTPResponse; begin f_Error := ''; // Used by OnRequestError try v_Response := f_NetHTTPRequest.Get(p_Url); f_StatusCode := v_Response.StatusCode; f_StatusText := v_Response.StatusText; Result := v_Response.ContentAsString; except on E: Exception do begin Result := ''; f_StatusCode := -1; f_StatusText := E.Message; end; end; end; procedure TisNetHttp.Post (const p_Url: String; var p_Strings: TStrings); var v_Response: IHTTPResponse; begin f_Error := ''; // Used by OnRequestError try v_Response := f_NetHTTPRequest.Post(p_Url,p_Strings); f_StatusCode := v_Response.StatusCode; f_StatusText := v_Response.StatusText; except on E: Exception do begin f_StatusCode := -1; f_StatusText := E.Message; end; end; end;
  9. I am using a TNetHttpRequest component to retrieve text from a secure website. I use the associated TNetHttpClient's OnAuthEvent to set the username and password. This allows me to retrieve text into a string using IHTTPResponse.ContentasString. I need to retrieve files from the same secured website. When I request the file using the same code, the IHTTPResponse.ContentStream seems to contain the requested file content since the IHTTPResponse.ContentLength appears to be correct. How can I save the content of IHTTPResponse.ContentStream to a file? Thanks
  10. I typecast the ContentStream as a TMemoryStream and SaveToFile worked perfectly. Thanks for your help.
  11. I can set the property of a component on a form by value: SetPropValue(TopPanel,'align',alBottom); I want to be able to set the property using the name of the value as a string: SetPropValue(TopPanel,'align','alBottom'); How can this be accomplished? Thanks
  12. egnew

    SetPropValue by Name

    KodeZwerg. I had a solution using just System.TypInfo, which works for all simple types. try SetPropValue(p_Component,p_PropertyName,p_PropertyValue); except ... But your version is better as it allows me to set all properties. Example: SetPropertyValue(TControl(MyFDConnection.Params),'UserName','MyUserName'); Thank you very much.
  13. I receive this warning when compiling a package: [dcc32 Warning] Indigo.EventHandlers.pas(11): W1055 PUBLISHED caused RTTI ($M+) to be added to type 'TisEventHandler' Documentation at https://docwiki.embarcadero.com/RADStudio/Sydney/en/W1055_Published_caused_RTTI_($M%2B)_to_be_added_to_type_'%s'_(Delphi)s says: The above description means nothing to me. How do I eliminate the warning from my package? Here is the code associated with the warning: type TisEventHandler = class published procedure isPrintScreenClick(Sender: TObject); end; Thanks
  14. I dynamically generate applications from text. This requires me to locate events by name. I am currently allowing either an event handler on a form or a built-in handler. The code I am using is this: function GetNotifyEvent (const p_MethodName: String; p_Form: TForm): TNotifyEvent; var v_Method: TMethod; begin v_Method.Data := Pointer(p_Form); v_Method.Code := p_Form.MethodAddress(p_MethodName); if v_Method.Code = nil then Result := BuiltInEvent(p_MethodName) else Result := TNotifyEvent(v_Method); end; Since I am using MethodAddress, I must publish methods I want to find by name. This is easy and is the approach I am using. I am open to other solutions. But my question is how to get rid of the warning from the compiler.
  15. I have a component in a Delphi package. The component contains a TTimer. When I manually create an instance of the Component, everything works fine. But when I drop the component on my form, everything works until the component tries to activate the timer. When the component attempts to start the timer with "FTimer.Enabled := True;", the access violation occurs. The following code from Vcl.ExtCtrls is called with Value=True but as soon as "if Value <> FEnabled" executes, the access violation occurs. procedure TTimer.SetEnabled(Value: Boolean); begin if Value <> FEnabled then begin FEnabled := Value; UpdateTimer; end; end Is this a bug or am I overlooking something? Thanks
  16. Dalija Prasnikar: You are the best. I have not built any components in years and forgot to add the override. That was the issue. Thanks so much.
  17. I populate forms dynamically from units. Attempts to create a TForm component on the Application.MainForm but has failed. My current workaround is to drop a "Host" panel on the MainForm and use it as the parent. procedure MakeSurface; MyComponent[0] := TPanel.Create; TPanel(MyComponent[0]).Parent := Application.MainForm; MyParent := MyComponent[0]; procedure MakeTopPanel MyComponent[1] := TPanfel.Create; TPanel(MyComponent[1]).Align := alTop; TPanel(MyComponent[1].Parent := MyParent; TPanel(MyComponent[1].Name := 'TopPanel'; Application.MainForm.FormCreate inherited; MakeSurface; MakeTopPanel; This fails with the message "Control 'TopPanel' has no parent window. Path: TopPanel." Application.MainForm.FormCreate inherited; MakeSurface; MyComponent[0].Parent := Self; MakeTopPanel; This works without a problem.
  18. This is not an issue because the first statement in the OnShow event is "OnShow := nil;" which keeps it from being called again. I would never call the OnShow event explicitly.
  19. Lajos Juhász - Thanks. I called inherited in the OnCreate event and never thought Delphi would set the MainForm property later. I moved the code to OnShow, and it works fine now. Thanks
  20. egnew

    SetPropValue by Name

    3) Not a problem. I realized I didn't need the list of values. I can try the provided value. An "invalid property element: <value>" exception is returned. However, if you have a solution, that would be useful. Thanks again.
  21. egnew

    SetPropValue by Name

    Thanks very much. 1 - Works Perfectly 2 - Works Perfectly 3 - Any idea how to the names of the type values? I can do it as shown in my previous email by specifying the type. I need to do it from the type name instead. 4 - I used this code to get the property type. Is there a better way? function GetPropertyType(p_Component: TComponent; const p_PropertyName: String): String; var v_Context: TRttiContext; v_Property: TRttiProperty; begin v_Property := v_Context.GetType(p_Component.ClassType).GetProperty(p_PropertyName); Result := v_Property.PropertyType.ToString; end; Thanks
  22. egnew

    SetPropValue by Name

    I checked the links you provided and do not yet see a solution. I can just use SetPropValue when the property is a simple type of sting, integer, float, etc. But here the property is a type. I am using Align as an example type, as it is a property of all TControl components: property Align: TAlign read FAlign write SetAlign default alNone; I dynamically create components at runtime using text commands and set the values of the components from the text. The command to create a panel aligned to TOP is "PANEL /ALIGN=TOP" which will be changed to "PANEL /ALIGN=alTOP" in the new version. Once I have this working, I can set any property in a component without knowledge of the component. In the current implementation, I have to deal with each property with a type using functions similar to this: function SetAlign(p_Control: TControl; p_Value: String); begin if SameText(p_Setting,'TOP') then p_Control.Align := alTop else if SameText(p_Setting,'BOTTOM') then p_Control.Align := alBottom etc... end; I validate a property name using this: function GetPropertyType(p_Component: TComponent; const p_PropertyName: String): String; var v_Context: TRttiContext; v_Property: TRttiProperty; begin v_Property := v_Context.GetType(p_Component.ClassType).GetProperty(p_PropertyName); Result := v_Property.PropertyType.ToString; end; I am attempting to resolve these problems: 1) How do I get the current value of the property? 2) How do I change the value of a property when the property is a type? That is the question posted here. 3) How do I validate the string that identifies the new property value? I can obtain valid strings for TAlign using the following: for I := Ord(Low(TAlign)) to Ord(High(TAlign)) do p_Memo.Lines.Add(GetEnumName(TypeInfo(TAlign),I)) However, I have not been able to determine how to do this for a type I don't know ahead of time using a parameter like: for I := Ord(Low(p_Type)) to Ord(High(p_Type)) do p_Memo.Lines.Add(GetEnumName(TypeInfo(p_Type),I)) Thanks
  23. I have a TEdgeBrowser log into a website that has PDF records we need to download to our system. When I enter the URL for the PDF, the PDF is loaded into the browser. How can I download the PDF instead of displaying it? Thanks, Sidney
  24. I am emailed html files that link to a Cisco "Secure Email Encryption Service". I can load the downloaded html into a TEdgeBrowser, manually enter the password, click the button, and access the content using document.innerHTML. I need to be able to get the program to fill in the password and click the submit button. Nothing happens when I execute the code I expect to work. I created the following a test form which uses the same input and button: <form action="https://www.google.com" method="post"> Password: <input name="key1" id="passwordInput1" autocomplete="off" type="password"><br> <input type="submit" "id="text_i18n.authframe.safr.button.openonline" value="Open Online" name="openButton"> </form> I set the password using: document.getElementById("passwordInput1").value = 'mypass' I click the button using: document.querySelector("input[type=submit]").click(); Everything works as expected with my test form but I am unable to accomplish this with the "Secure" form. This is likely because the values are embedded in other elements. The structure is very complex and I am leaving out many table rows and other details. I think I have identified the important elements as shown below: <table id="outerTable" <tbody> <tr> <td id="iframelocation"> <iframe id="authFrame" #document >html> <body> <form> <table class="mainWIndow"> <tbody> <tr> <td id="heightControl" <table> <tbody> <tr> <td> <table id=borderTable"> <tbody> <tr> <td id=passwordEntry1"> <p> <input size="20" maxlength="127" name="key1" id="passwordInput1" placeholder="Secure Email Encryption Service Password" title="Enter your password here" autocomplete="off" type="password" style="width: 100%;"> </input> The button is in <td id="openButtonLocation"> in <td id="buttonContainer" eventually going batck to <tr id=buttonRow", etc. and is defined as: <input type="submit" id="text_i18n.authframe.safr.button.openonline" value="Open Online" name="openButton" onclick="openOnline()" class="btn"> The ExecuteScript seems to only execute one line of javascript so I am not sure how to access the field and the button. What do I need to do differently? I would also like to be able to accomplish this using the types (password and submit) instead of the Id once I get this working. Thanks
  25. I deleted invalid paths in "tools options language Delphi library" for "library path", "browsing path", and "Debug DCU path" in both Win-32 and Win-64. Compiling units with "Window" in the use clause fails with the message "[dcc32 Fatal Error] Form.pas(10): F2613 Unit 'Windows' not found." Any unit from WinAPI such as Messages results in similar fatal errors. I can bypass the error by using "WinAPI.WIndows, WinAPI.Messages," instead of "WIndows, Messages". System units such as "SysUtils" can still be used without the need to qualify as "System.SysUtils". I am using RadStudio 11 Alexandria.. Is there a way to resolve this issue without reinstalling? Thanks
×