I get this error when I'm executing a web service method. Unfortunately, there's not much more detail than that. The method takes parameters including arrays of COM objects, a byref string, and a byref object. There are also some parameters that are null. How would I know which one of these is causing the problem? I suspect it may be due to the null reference if the method is expecting an object, but the web service documentation indicates that null is acceptable. The WSDL is https://test.mdtoolboxrx.net/rxws/rx.asmx?wsdl. Here is my sample:
DO rxproxy
public loService as rxproxy
loservice = CREATEOBJECT("rxproxy")
Public loAccount as rx.Account
loAccount = loservice.obriDGE.createinstance("rx.Account")
loAccount.AccountId = 9999
loAccount.AccountAuthKey = "asdfasdf"
loAccount.PracticeId = "ASDFD"
loAccount.PracticeName = "ASDF"
loAccount.UserId = "31"
loAccount.username = "asdfsdaf"
loPrescribers = loservice.obriDGE.createarray("rx.Prescriber")
loPrescriber = loPrescribers.CreateItem()
loPrescriber.ID = "31"
loPrescriber.LastName = "asdfasdf"
loPrescriber.FirstName = "asdfasdf"
loPrescriber.MiddleName = "asdfasdf"
loPrescriber.Suffix = "sa"
loPrescriber.NPI = "asdfdf"
loPrescriber.DEA = "asdfadf"
loPrescriber.LocationId = "FV"
loPrescriber.ClinicName = "asdfasdf"
loPrescriber.Addr1 = "asdfadf"
loPrescriber.City = "asdfasdf"
loPrescriber.State = "as"
loPrescriber.Zip = "12312"
loPrescriber.Phone = "1112312311"
loPrescriber.Fax = "1112312311"
loPrescriber.Current = .t.
loPrescriber.UserId = "31"
loPrescriber.username = "ASDF"
loPrescribers.AddItem(loPrescriber)
loLocations = loservice.obriDGE.createarray("rx.Location")
loLocation = loLocations.CreateItem()
loLocation.ID = "FV"
loLocation.ClinicName = "assdfdsf"
loLocation.Addr1 = "SZADFSDFDSF"
loLocation.City = "asfasdf"
loLocation.State = "AS"
loLocation.Zip = "12312"
loLocation.Phone = "1223512311"
loLocation.Fax = "1223512311"
loLocation.Current = .t.
loLocations.AddItem(loLocation)
Public loPatient as rx.Patient
loPatient = loservice.obriDGE.createinstance("rx.Patient")
loPatient.ID = "200089"
loPatient.LastName = "TEST"
loPatient.FirstName = "TEST"
loPatient.Gender = "M"
loPatient.DOB = CTOT('01/01/1940')
loPatient.Zip = "93291"
PUBLIC loStatus as rx.Status
loStatus = loservice.obrIDGE.createinstance("rx.Status")
PUBLIC loStatusString
loStatusbool = .f.
loStatusString = SPACE(255)
loservice.oService.UpdateDataForScreens(loPrescribers, loLocations, loPatient,.null.,.null. ,.null. ,.null. , .f.,CTOD('01/01/2001'),null,loAccount,@loStatus,@loStatusString)
I don't know - you need to get a working request going to know for sure. The samples on the ASMX preview page show all objects filled in.
.NULL.
should be find for values that are indeed nullable.
+++ Rick ---
It does look like they allow nulls, since they gave the following as an example (though it looks like this may be with an older sequence):
Dim authcode as String = objMDTB.UpdateDataForScreens(Prescribers, Locations, Nothing, PatObject, Nothing, Nothing, Nothing, Nothing, True, EncDate, Prescribers(curdocidx), Acct, Status, Msg)
Do you think it is an issue with passing the COMArrays, or maybe the by reference parameters? The problem is I don't even know which parameter has the problem. Appreciate any guidance. Would love to purchase the proxy generator if I can get it to work.
And I tried generating empty array objects for the parameters I was otherwise doing nulls for, and still the same problem.
The signature of the method has to be matched, so I think null parameters could be a problem.
Look at the exact signature for the .NET Method in Reflector and see what it says, then make sure you match the signature exactly. The Ref parameters won't do anything unless you call the .NET method directly (rather than the .invokemethod() proxy).
+++ Rick ---
I think it has to be with the array. I tried a simpler method, and still the same problem:
DO rxproxy
local loService as rxproxy
loservice = CREATEOBJECT("rxproxy")
local loAccount as rx.Account
loAccount = loservice.obriDGE.createinstance("rx.Account")
loAccount.AccountId = 999
loAccount.AccountAuthKey = "999"
loAccount.PracticeId = "ASDFINC"
loAccount.PracticeName = "ASDF, Inc."
loAccount.UserId = "31"
loAccount.username = "Bob"
loVitalRecords = loservice.obriDGE.createarray("rx.VitalRecord")
loVitalRecord = loVitalRecords.CreateItem()
loVitalRecord.BPDia = 90
loVitalRecord.BPSys = 120
loVitalRecords.AddItem(loVitalRecord)
local loStatus as rx.Status
loStatus = loservice.obrIDGE.createinstance("rx.Status")
local lcStatusString
lcStatusString = SPACE(255)
loService.updatedatapatientvitals('200089',loVitalRecords,loAccount,@lostatus,@lcStatusString)
Passing by reference is almost certainly not correct. You're passing objects and these are reference objects anyway but passing by reference will screw up the COM marshalling.
+++ Rick ---
I see. Is there any way to get this done with the web service proxy generator then?
The problem are the out parameters. I will never understand why anybody would build a service that can return an object and use out
parameters.
Looks like you need to use CreateComValue()
to create the proxy value for the Out parameters.
Note that you can't use direct call method described in this topic, since you're passing changed arrays back and .NET will not recognize the COM Arrays directly - you'll have to go through InvokeMethod()
.
Also - status isn't an object, it's an enum so that needs to be handled differntly as well. And it's an out parameter.
+++ Rick ---
Ok, so I took a swipe at this.
The following code works:
DO rxproxy
local loService as rxproxy
loservice = CREATEOBJECT("rxproxy")
loBridge = loService.oBridge
local loAccount as rx.Account
loAccount = loservice.obriDGE.createinstance("rx.Account")
loAccount.AccountId = 999
loAccount.AccountAuthKey = "999"
loAccount.PracticeId = "ASDFINC"
loAccount.PracticeName = "ASDF, Inc."
loAccount.UserId = "31"
loAccount.username = "Bob"
loVitalRecords = loservice.obriDGE.createarray("rx.VitalRecord")
loVitalRecord = loVitalRecords.CreateItem()
loVitalRecord.BPDia = 90
loVitalRecord.BPSys = 120
loVitalRecords.AddItem(loVitalRecord)
*** Out Enum Value
local loStatus as rx.Status
loStatus = loBrIDGE.CreateComValue()
loStatus.SetEnum("rx.Status.Success")
*** Out string
loStatusString = loBridge.CreateComValue(SPACE(255))
* public string UpdateDataPatientVitals(string PatientId, VitalRecord[] PatientVitals, Account AccountObj, out Status StatusFlag, out string StatusMsg)
*** Use InvokeMethod so ComArray and ComValue are properly parsed
*** into .NET objects
lcResult = loBridge.InvokeMethod(loService.oService,"updatedatapatientvitals",'200089',loVitalRecords,loAccount,lostatus,loStatusString)
*** result is a string (WTF?)
? lcResult
? loService.cErrorMsg && empty
? loStatus.Value 0 - enum Error value
? loStatusString.Value - COM Value
The problem is that the out
parameter cause problems for the generator - there's no way to reliably determine the types required so it generates the @
ref parameters - this works with simple parameters, but it doesn't if you need to pass arrays or other wrapper objects so the generated code is off.
You can directly call InvokeMethod()
as shown above using ComValue
objects for out parameters. You can test this just as I do above and then update the signature in the generated proxy to match once you get it working.
Basically fix like this:
************************************************************************
* UpdateDataPatientVitals
****************************************
FUNCTION UpdateDataPatientVitals(PatientId as String,PatientVitals as VitalRecordArray,AccountObj as Account,StatusFlag as Status,StatusMsg as String) as String
LOCAL loException as Exception, lvResult as String
THIS.lError = .F.
this.cErrorMsg = ""
lvResult = .F.
TRY
*lvResult = this.oService.UpdateDataPatientVitals(PatientId,PatientVitals,AccountObj,StatusFlag,StatusMsg)
*** Change to this! ***
lvResult = this.oBridge.InvokeMethod(this.oService, "UpdateDataPatientVitals", PatientId,PatientVitals,AccountObj,StatusFlag,StatusMsg)
CATCH to loException
THIS.GetErrorDetail(loException)
ENDTRY
RETURN lvResult
ENDFUNC
* UpdateDataPatientVitals
And then you can use the original method call:
loResult = loService.updatedatapatientvitals('200089',loVitalRecords,;
loAccount,lostatus,loStatusString)
Unfortunately this service has those bloody out parameters on every method so effectively you have to fix up the entire proxy. But pretty easy with a few search and replace operations.
+++ Rick ---
FWIW, I'm making some changes to the proxy generator to no longer generate the ref parameters with @
in the ref format. Instead there will be ref_
prefix to make it clear that these are reference parameters and then the docs have info on using the ComValue object.
+++ Rick ---
It worked!!!! Thanks! I'm not entirely sure the reference objects are getting filled properly, but I'm hopeful that I can still make it work without it. The null values ended up being completely fine. With the changes you're making, will the edits to the proxy program still be needed?
I've updated the tools to generate the ref_
syntax, so using the new tooling you won't have to fix up the generated code. Just reinstall and reimport the service.
You will have to create the ComValue
as shown in the example and since all methods have those parameters you probably will want to create a helper method that sets these two parameters for you.
+++ Rick ---
Sorry, one more issue, not sure what is causing it. This doesn't work:
lcRet = loservice.GetPrescriptionsForPatientXML("200089",loAccount,loStatus,lcStatusString)
But this does work:
lcRet = lobridge.invokemethod(loservice,"GetPrescriptionsForPatientXML","200089",loAccount,loStatus,lcStatusString)
Any ideas?
Just that one method?
If the generator is working correctly the signature for the method you are calling in the proxy should be running exactly the same code as your direct wwDotnetBridge invokation. Please check the source to the method that was generated and the InvokeMethod()
call - what does that look like?
+++ Rick ---
I haven't tried other methods since this is I think the only other one I need, but here's the generated source code. The direct invocation returns .f., but invokemethod returns the appropriate XML string.
CS file:
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://mdtoolboxrx.com/GetPrescriptionsForPatientXML", RequestNamespace="http://mdtoolboxrx.com/", ResponseNamespace="http://mdtoolboxrx.com/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public string GetPrescriptionsForPatientXML(string PatientId, Account AccountObj, out Status StatusFlag, out string StatusMsg) {
object[] results = this.Invoke("GetPrescriptionsForPatientXML", new object[] {
PatientId,
AccountObj});
StatusFlag = ((Status)(results[1]));
StatusMsg = ((string)(results[2]));
return ((string)(results[0]));
}
Generated VFP class:
FUNCTION GetPrescriptionsForPatientXML(PatientId as String,AccountObj as Account,ref_StatusFlag as Status,ref_StatusMsg as String) as String
LOCAL loException as Exception, lvResult as String
THIS.lError = .F.
this.cErrorMsg = ""
lvResult = .F.
TRY
lvResult = this.oBridge.InvokeMethod(this.oService, "GetPrescriptionsForPatientXML", PatientId,AccountObj,ref_StatusFlag,ref_StatusMsg)
CATCH to loException
THIS.GetErrorDetail(loException)
ENDTRY
RETURN lvResult
ENDFUNC
I dunno. That's looks like the same code to me. What does .cErrorMsg
return?
I suspect there's something else going on here - maybe you're passing different values that's making one fail over the other?
Please step through the code and see what actually fails - you should see the exception fire in the proxy method.
Also - it works on the other method you're trying this on? (ie. using the proxy method).
+++ Rick ---
I must have had something in use when I copied it over because it's most definitely working now when I redid it. Thanks! Has the purchased version been updated? If so, I'll go ahead and purchase it. There's another integration project we are working on that uses post, so we me may be looking into the Internet & Client Tools package as well. Thanks for all your help!