My Delphi XE6 project needs to call SOAP-based webservices. Unfortunately it seems that there may be a problem performing such webservice calls from 64-bit DLLs under IIS, which is how my project will be deployed.
See for example the following public webservice:
http://www.webservicex.net/geoipservice.asmx?WSDL
After creating a test ISAPI webserver project and importing the WSDL above, I tried using the following code to make a webservice call:
uses activeX, geoipservice;
procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
CountryName : string;
I : GeoIPServiceSoap;
G : GeoIP2;
begin
CoInitialize(nil);
try
CountryName := 'unassigned';
I := GetGeoIPServiceSoap();
if assigned(I) then begin
G := I.GetGeoIP('...insert IP address here...');
if assigned(G) then begin
countryname := G.CountryName;
end;
end;
Response.Content :=
'<html>' +
'<head><title>Web Service Test</title></head>' +
'<body>Country: ' + CountryName + '</body>' +
'</html>';
except
on E : exception do begin
Response.Content :=
'<html>' +
'<head><title>Web Service Test</title></head>' +
'<body>Exception: ' + E.ClassName() + ' ' + E.Message + '</body>' +
'</html>';
end;
end;
CoUninitialize();
end;
When I run the project as a 32-bit ISAPI DLL, viewing the website in a browser results in a country name being displayed as expected. When I run the project as a 64-bit ISAPI DLL, viewing the website in a browser results in the following error message being displayed (Zugriffsverletzung = access violation):
Exception: EAccessViolation Zugriffsverletzung bei Adresse
000000C700492460. Schreiben von Adresse 000000C700492460
This seems very similar to the following problem:
Delphi XE2 64 bit ISAPI Access Violation
However the problem there relates to Delphi XE2, and it is stated that upgrading to Delphi XE4 fixed the problem. I have tested in both Delphi XE4 and XE6 and the problem occurs in both.
Has anybody else encountered this problem or has an idea how to solve it?
Setup: Delphi XE6, Win8.1, IIS 8.5
I believe the problem is related to wininet. Delphi uses Wininet (Via HTTPRIO) to connect to SOAP Servers (see Soap.SOAPHTTPTrans.pas). But Microsoft says that using Wininet inside a service (such as IIS) is not supported, that's why it works on a console application. Services should use WinHTTP.
https://support.microsoft.com/en-us/help/238425/info-wininet-not-supported-for-use-in-services
I had the same problem and tried to convert the Wininet calls to WinHTTP, but in the end I gave up on Delphi for this use case, too much trouble for such an expensive tool.
Related
I need to build an interface between Dynamics NAV 2013 and Groupon API V2
It seems to me that Groupons API data comes in json format - how could I get this information in Dynamics NAV (orders for example) ?
Should I use webservices ?
Thanks
EDIT :
I worked a lot on this and got receiving data from groupon working
The problem is send information :
I have a problem to send a post request with parameters - this is my code :
WebServiceURL := 'https://...';
Request := Request.Create(WebServiceURL);
Request.Method := 'POST';
Request.KeepAlive := TRUE;
Request.Timeout := 30000;
Request.Accept('application/json');
Request.ContentType('multipart/form-data');
postString := 'param1=123¶m2=456';
Request.ContentLength := STRLEN(postString);
StreamWriter := StreamWriter.StreamWriter(Request.GetRequestStream);
StreamWriter.Write(postString);
StreamWriter.Close;
I get a 500 error so I don't know anything about why its rejected
But if there is something that seems to be wrong to you, please help !
the most NAV frienly way is to get the order in XML format from the API and import the XMLs using XMLports or Codeunits(use DotNet)
Cheers
You don't need Nav web services because in this case you are (Nav) the client side, when web services is to build the server side. E.g. you can call web service but web service can not call anything. Most probably you will use NAS to perform tasks periodically.
AFAIK Nav can't handle JSON, but in Nav2013 it's possible to use .Net libraries so just pick JSON library you like and call it from Nav to process responses from API.
To make calls(requests) to API you can use .net or com library of your choise just the same was as for JSON.
ReqXML : Automation 'Microsoft XML, v6.0'.DOMDocument60
RespXML: Automation 'Microsoft XML, v6.0'.DOMDocument60
Req : Automation 'Microsoft XML, v6.0'.XMLHTTP60
CREATE(Req, TRUE);
Req.open(reqType, Uri, FALSE);
Req.setRequestHeader('contentType', 'text/xml; charset=UTF-16');
CASE reqType OF
'GET': Req.send();
'POST': Req.send(ReqXML);
END;
RespText := Req.statusText;
IF Req.status <> 200 THEN EXIT(FALSE);
IF ISCLEAR(RespXML) THEN CREATE(RespXML, TRUE);
RespXML.load(Req.responseXML);
In this example request to address stored in Uri is made. If you need to post some data aside from URL parameters then you put it to ReqXML. If API is suppose to return something it will be inside RespXML.
This code works for older versions of Nav. You will have to rewrite it a little to use .Net libraries (like webclient) and maybe get rid of XML (in my case API was XML based) but the structure will be pretty much the same.
I'm trying to use the Vix API and i'm trying to receive some information of the virtual machines i'm working on. The information i need is the domain to which the VM is connected, and the list of users that are registered (i.e., have access) to the specific VM.
I tried using 'google' and the documentation (https://www.vmware.com/support/developer/vc-sdk/visdk2xpubs/ReferenceGuide/) but i can't find how to get this information. Tried to allocate a few objects that might contain the the domain, but it didn't help.
I'm not sure if this will help you, but below is an extract from a small Delphi
project (which uses the same Vix COM/OLE interface as vbScript) which retrieves
the current user name from the Environment of a Win7 VM. Hopefully the code
would be straightforward to translate to vbScript or VBA. Obviously,
you can retrieve the value of any other Environment variable, such as the User Domain, in a similar way.
I've had quite a thorough look through the Vix COM API and have not found anything which would retrieve a list of available login IDs for a VM. So,
if I had to do this, I'd write a small utility app to run in the VM to get them. (You may know already, but it's straightforward to run an app in the VM via the Vix interface.)
Code
type
TForm1 = class(TForm)
[...]
VixLib : IVixLib;
Job : IJob;
Host : IHost;
VM : IVM;
Err : Int64;
vWaitParams : OleVariant;
vResults : OleVariant;
vValue : OleVariant;
Msg : String;
end;
[...]
procedure TForm1.GetUserName;
begin
// Prior to calling this code, you need to have successfully called
// LogInGuest on the VM via Vix
// change USERNAME to USERDOMAIN in the following line to get the domain
Job := VM.ReadVariable(VIX_GUEST_ENVIRONMENT_VARIABLE, 'USERNAME', 0, Nil);
vWaitParams := VarArrayOf([VIX_PROPERTY_JOB_RESULT_VM_VARIABLE_STRING]);
Err := Job.Wait(vWaitParams, vValue);
if Err <> 0 then
raise Exception.Create('Error %d', [Err]);
Msg := vValue[0];
Caption := Msg;
end;
I'm using Delphi XE7 + Employee Interbase DB.
I'm tryin to build a SOAP server with Datasnap.
I have 3 SOAP Server Data Modules in one server, each one with a table: Customer, Employee, Country, each one made the same way (File, new, other, SOAP Server Data Module), then from Data Explorer dragging a table on the data module (Delphi creates a TSQLCOnnection and a TSQLDataset), then adding a TDatasetProvider. The three Data Modules are identical.
In the client application i have three interfaces made by WSDL importer
ICountryWM = interface(IAppServerSOAP)
['{1DA001BC-4DFB-E7F8-F62D-DA3545B334DC}']
end;
The three interfaces are identical (of course names and GUIDS are different).
In the Client form i have three buttons, they doperform the same operations:
procedure TClientFM.BtnEmployeeClick(Sender: TObject);
begin
ClientDataSet.Close;
SoapConnection.Close;
SoapConnection.UseSOAPAdapter := False;
SoapConnection.SOAPServerIID := '{02E9AB9B-DB17-ACF5-68D9-73FC763C04DC}';
SoapConnection.URL := 'http://localhost:8080/soap/IEmployeeWM';
ClientDataset.ProviderName := 'EmployeeProvider';
SoapConnection.Open;
ClientDataSet.Open;
end;
only changing ServerID, URL, ProviderName.
The three DatasetProvider ini the three Data Module have "Exported" checked.
In the beginning the first SOAP Server Data Module (Customer) worked well: from my client i sawe the Customers table. When i tried Employee or Country, i always got "Provider not exported".
I made a BIG MISTAKE: trying to change something i unchecked the Exported flags, compiled and ran server and client: nothig worked.
Ok, i expected that.
Then I rechecked the flags ... now the Exported flags are checked but no one provider works correctly ... (yes, the one that worked now does not work).
Both Server and Client run in the same PC.
Can someone give me any idea of what is wrong?
I think the problem is in the server, but WHERE in the server?
Is it possible to connect to a web service (for example send a HTTP Request) via VBA in Microsoft Access?
For example, the user clicks a button on a form, then a HTTP Request is sent to a web service that responds with OK.
Has anyone done this before?
Note: VBA, not VB.NET.
This is code I've used quite successfully with Access 2003. It's from the interwebs, copied and re-copied ages ago. It creates a XMLHttpRequest Object, sends an HTTP GET request, and returns the results as a string.
Public Function http_Resp(ByVal sReq As String) As String
Dim byteData() As Byte
Dim XMLHTTP As Object
Set XMLHTTP = CreateObject("MSXML2.XMLHTTP")
XMLHTTP.Open "GET", sReq, False
XMLHTTP.send
byteData = XMLHTTP.responseBody
Set XMLHTTP = Nothing
http_Resp = StrConv(byteData, vbUnicode)
End Function
sReq is the URL; the function returns the response. You may need to make sure ActiveX Data Objects are enabled under your References (in the VBA editor, go to Tools > References).
This is the code , which I used. You need to first reference Microsoft XML V6 for this code to work.
Public Sub GetPerson()
'For API
Dim reader As New XMLHTTP60
reader.Open "GET", "www.exmple.com/users/5428a72c86abcdee98b7e359", False
reader.setRequestHeader "Accept", "application/json"
reader.send
Do Until reader.ReadyState = 4
DoEvents
Loop
If reader.Status = 200 Then
Msgbox (reader.responseText)
Else
MsgBox "Unable to import data."
End If
End Sub
I have used the "Microsoft Office 2003 Web Services Toolkit 2.01" toolkit (available here) on a few projects. It worked pretty well for me, although I also wrote the web services it was talking to, so I had the luxury of being able to fiddle with both ends of the process when getting it to actually work. :)
In fact, I just upgraded one of those apps from Access_2003 to Access_2010 and the SOAP client part of the app continued to work without modification. However, I did encounter one wrinkle during pre-deployment testing:
My app would not compile on a 64-bit machine running 32-bit Office_2010 because it did not like the early binding of the SoapClient30 object. When I switched to using late binding for that object the code would compile, but it did not work. So, for that particular app I had to add a restriction that 64-bit machines needed to be running 64-bit Office.
Also, be aware that Microsoft's official position is that "All SOAP Toolkits have been replaced by the Microsoft .NET Framework." (ref. here).
Okay, we've got an application which consists of a website hosting several ASMX webservices, and a handheld application running on WinMo 6.1 which calls the webservices.
Been developing in the office, everything works perfect.
Now we've gone to install it at the client's and we got all the servers set up and the handhelds installed. However the handhelds are now no longer able to connect to the webservice.
I added in extra code in my error handler to specifically trap WebException exceptions and handle them differently in the logging to put out extra information (.Status and .Response).
I am getting out the status, which is returning a [7], or ProtocolError. However when I try to read out the ResponseStream (using WebException.Response.GetResponseStream), it is returning a stream with CanRead set to False, and I thus am unable to get any further details of what is going wrong.
So I guess there are two things I am asking for help with...
a) Any help with trying to get more information out of the WebException?
b) What could be causing a ProtocolError exception?
Things get extra complicated by the fact that the client has a full-blown log-in-enabled proxy setup going on-site. This was stopping all access to the website initially, even from a browser. So we entered in the login details in the network connection for HTTP on the WinMo device. Now it can get to websites fine.
In fact, I can even pull up the webservice fine and call the methods from the browser (PocketIE). So I know the device is able to see the webservices okay via HTTP. But when trying to call them from the .NET app, it throws ProtocolError [7].
Here is my code which is logging the exception and failing to read out the Response from the WebException.
Public Sub LogEx(ByVal ex As Exception)
Try
Dim fn As String = Path.Combine(ini.CorePath, "error.log")
Dim t = File.AppendText(fn)
t.AutoFlush = True
t.WriteLine(<s>===== <%= Format(GetDateTime(), "MM/dd/yyyy HH:mm:ss") %> =====<%= vbCrLf %><%= ex.Message %></s>.Value)
t.WriteLine()
t.WriteLine(ex.ToString)
t.WriteLine()
If TypeOf ex Is WebException Then
With CType(ex, WebException)
t.WriteLine("STATUS: " & .Status.ToString & " (" & Val(.Status) & ")")
t.WriteLine("RESPONSE:" & vbCrLf & StreamToString(.Response.GetResponseStream()))
End With
End If
t.WriteLine("=".Repeat(50))
t.WriteLine()
t.Close()
Catch ix As Exception : Alert(ix) : End Try
End Sub
Private Function StreamToString(ByVal s As IO.Stream) As String
If s Is Nothing Then Return "No response found."
// THIS IS THE CASE BEING EXECUTED
If Not s.CanRead Then Return "Unreadable response found."
Dim rv As String = String.Empty, bytes As Long, buffer(4096) As Byte
Using mem As New MemoryStream()
Do While True
bytes = s.Read(buffer, 0, buffer.Length)
mem.Write(buffer, 0, bytes)
If bytes = 0 Then Exit Do
Loop
mem.Position = 0
ReDim buffer(mem.Length)
mem.Read(buffer, 0, mem.Length)
mem.Seek(0, SeekOrigin.Begin)
rv = New StreamReader(mem).ReadToEnd()
mem.Close()
End Using
Return rv.NullOf("Empty response found.")
End Function
Thanks in advance!
I was not able to get any more information out of the WebException class. For some reason that GetResponseStream always returns an unreadable stream.
I was able to narrow the problem down to proxy authentication though by writing a separate little program which simply tried to request a web page and read the response and put it into a textbox.
This program returned a response of proxy authentication required. I find this really weird though because I put the proxy information into the network connection settings on my device. I would've though this would apply to all network traffic going out on that connection; apparently it only applies to traffic through the browser, not application requests. Odd.