I'm building a Delphi Win32 app that should consume a Soap Service, which turns out to be a .NET based app. One function returns a DataTable. Of course, Delphi Win32 (not Delphi .NET) has no way of understanding this natively.
Any way I can make it work? I'll be happy to parse the XML manually too, but I've no idea how to get hold of the raw XML response.
The WSDL: https://stratus.voxamvia.co.za/api.asmx?WSDL
The function: GetNotifications which returns GetNotificationsResult, which builds as:
GetNotificationsResult = class(TRemotable)
private
Fnamespace: WideString;
Fnamespace_Specified: boolean;
FtableTypeName: WideString;
FtableTypeName_Specified: boolean;
procedure Setnamespace(Index: Integer; const AWideString: WideString);
function namespace_Specified(Index: Integer): boolean;
procedure SettableTypeName(Index: Integer; const AWideString: WideString);
function tableTypeName_Specified(Index: Integer): boolean;
published
property namespace: WideString Index (IS_ATTR or IS_OPTN) read Fnamespace write Setnamespace stored namespace_Specified;
property tableTypeName: WideString Index (IS_ATTR or IS_OPTN) read FtableTypeName write SettableTypeName stored tableTypeName_Specified;
end;
Any help appreciated!
Would it help if I implement RemObjects?
You can build your dataset from xml. This should give you a starting point: http://www.gekko-software.nl/DotNet/Art07.htm and http://www.gekko-software.nl/DotNet/Art08.htm.
I haven't used DataAbstract from RemObjects, so I can not give an advice on it.
LE: you can access and consume a web service written in .net by following this simple article well written by drbob - Consuming C# Web Services with Delphi 7 Professional
which contains also a small example on how to build dynamically and how to use the THttpRio (is the same as the Mikael Eriksson's answer)
Any way I can make it work? I'll be happy to parse the XML manually
too, but I've no idea how to get hold of the raw XML response.
You can get it in the OnAfterExecuteEvent on your THTTPRIO component. There you will have SOAPResponse: TStream as a parameter.
Update:
To get the event to fire you add a THTTPRIO component, create the event handler and use the RIO component as the third parameter to GetAPISoap.
This worked for me. HTTPRIO1 is a component on the form.
procedure TForm7.Button1Click(Sender: TObject);
var
A: APISoap;
begin
A := GetAPISoap(False, '', HTTPRIO1);
A.Validate_User('', '');
end;
Related
I cannot figure out which parameter to pass to TDataSetProvider.ApplyUpdates
The definition says:
function ApplyUpdates(const Delta: OleVariant; MaxErrors: Integer; out ErrorCount: Integer): OleVariant;
What should I pass to the parameter Delta? I cannot find any example in the delphi documentation.
I am usign dbexpress and I want to call ApplyUpdates to apply the changes made to the records to the database.
If the provider and the clientdataset are connected (by setting the provider property of the CDS), changes are applied by
CDS.ApplyUpdates(0);
In the following type:
MyClass = class(TInterfacedPersistent)
private
FMyProperty: Integer;
published
procedure setMyProperty(Value: Integer); virtual;
property MyProperty: Integer read FMyProperty write setMyProperty;
I would like to know the name of the setter method of the "MyProperty" property via RTTI. I've tried the following:
procedure ShowSetterMethodsNames(pMyObject: TObject);
var
vPropList: TPropList;
vCount, I: Integer;
begin
vCount:= GetPropList(pMyObject.ClassInfo, tkProperties, #vPropList);
for I:= 0 to vCount -1 do
begin
if Assigned(vPropList[I]^.SetProc) then
ShowMessage(pMyObject.ClassType.MethodName(vPropList[I]^.SetProc));
end;
end;
Although the pointer is not nil, all I have is an empty message. Does anybody have some tip to me?
P.S.: I'm using Delphi XE4, and I know I should use extended RTTI instead of classic, but anyway, I can't do what I want in both features... So, any help will be appreciated. Thanks for the replies.
FINAL EDITION, problem solved:
Here is the code working, based in the (help of my friends and...) RTTI unit (DoSetValue method of TRTTIInstanceProperty class):
procedure ShowVirtualSettersNames(pObject: Pointer);
var
vSetter, vPointer: Pointer;
vPropList: TArray<TRttiProperty>;
vProp: TRttiProperty;
begin
vPropList:= RTTIUtils.ExtractProperties(TObject(pObject).ClassType); // Helper to get properties from a type, based in extended RTTI
for vProp in vPropList do
begin
vPointer:= TRttiInstanceProperty(vProp).PropInfo^.SetProc;
vPointer:= PPointer(PInteger(pObject)^ + Smallint(vPointer))^;
ShowMessage(TObject(pObject).ClassType.MethodName(vPointer));
end;
end;
This ONLY WORKS FOR VIRTUAL SETTERS, for statics the message is empty. Thanks everyone!
You can retrieve this method name, if
a) move the method to the published section (classic RTTI works with this section only (more accurately - compiled with {$M+} directive))
b) use right class specifier - MyClass.MethodName, because MethodName is class function
This code works on D7 and XE3:
MyClass = class(TInterfacedPersistent)
private
FMyProperty: Integer;
published
procedure setMyProperty(Value: Integer);
property MyProperty: Integer read FMyProperty write setMyProperty;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
ppi: PPropInfo;
begin
ppi := GetPropInfo(MyClass, 'MyProperty');
ShowMessage(MyClass.MethodName(ppi.SetProc));
end;
P.S. What Delphi version are you using? What about Extended RTTI (since D2010)?
Read c:\rad studio\9.0\source\rtl\common\System.Rtti.pas
procedure TRttiInstanceProperty.DoSetValue
The setter of the property may be
a field (variable)
a static procedure
a virtual procedure (your case)
And those cases make PropInfo^.SetProc have different semantics of its value.
Direct address only applies to static procedures. For virtual methods you add a VMT offset and take the code address from that memory cell, as specified in that code i mentioned (but would not quote for copyright reasons).
Or you just could use TRttiProperty.SetValue and let Delphi do all those little under the hood details. See http://docwiki.embarcadero.com/Libraries/XE2/en/System.Rtti.TRttiProperty.SetValue
EDIT:
the code removed - it did not worked verbatim and the topic starter provided working version.
Regarding and I know I should use Extended RTTI instead of classic one - that is questionable claim. Extended RTTI is known to work noticeably slower than classic one. Dunno if someone did profiled it, but i suspect that is mostly due to the slow code of TValue. You can google and find that lot of people complained of slow TValue implementation and provided alternative ones with fixed efficiency. However since Extended RTTI only uses stock TValue it cannot benefit from those implementations and remains slower than classic one.
I am developing application which intend to be cross platform. I used to use Windows Messages but now I am dropping it out. I replaced messages with callbacks but regardless I can use different technologies I am not aware of different possibilites when not using windows messages.
Well I have main exe aplication and some dll plugins. I have some objects and threads in dll and I would like to notify main application about some changes that DLL made to data structure.
As I said I am currently working with some callbacks. To provide compatibility with different languages (C++, VB, C#) I have non-object type of callback. I am not sure if other languages supports callback of object.
So my questions are:
What are the alternatives (cross-platform) to windows messages? Can callbacks replace messages?
Do other languages support callback of object?
I guess other languages have different technologies as alternative to messages?
You can certainly use callback functions instead of messages. You can't use callback methods because only Delphi and C++ Builder understand how to invoke Delphi method pointers. However, you can use callback objects with any language that supports COM. Here's an example for a plug-in to notify the application that the data structure has changed:
Define an interface.
type
IDataStructureChanged = interface
['{GUID}']
procedure Call; stdcall;
end;
You could add some parameters to the method so the plug-in can tell how the data structure changed, or pass some value indicating which plug-in is making the notification.
Implement it in the application.
type
TDataStructureChangedListener = class(TInterfacedObject, IDataStructureChanged)
private
FForm: TForm;
procedure Call; stdcall;
public
constructor Create(Form: TForm);
end;
When you instantiate that class, you can pass it a reference to your program's main form, or whatever other information your program will need to be able to take action when a plug-in eventually calls the Call method. Implement Call to make your application do whatever it needs to do when a data structure changes.
Pass a reference to each of the plug-ins when you initialize them.
ChangeListener := TDataStructureChangedListener.Create(Self);
for i := 0 to Pred(PlugIns.Count) do
PlugIns[i].Init(ChangeListener);
The plug-in should store a reference to the listener object, and when the data structure changes, it can call the Call method to notify your application.
What I've described here is what's generally known as an event sink. You can have more than one in your program. If there are multiple events to handle, you could have a separate interface for each kind of event, or you could group them all into a single interface and have a different method for each event. You could have a different sink object for each plug-in, or you could give each plug-in a reference to the same sink object, and then pass a plug-in-ID parameter.
I would definately use callbacks. The main app could give a callback function to the DLL to call when needed, and then the callback function itself can send window messages to the app if it needs to.
I agree with Remy, (!). A straightforward callback allows the handler to implement any kind of further communication it chooses - it might post a message, it may push a parameter onto a queue, whatever it wants. If you want to be cross-platform, you are going to have to resort to passing in, and out, simple types. It's usual to pass in a 'user context' pointer when callbacks are set up. The callback passes this pointer into the handler. This allows callers to pass in a context object as a pointer/int and to recover it in the handler, (by casting the pointer/int back to an object). The handler can then call methods on the context, no matter whether it's Delphi, C++ etc.
So my questions are:
What are the alternatives (cross-platform) to windows messages? Can callbacks replace messages?
Yes you can replace messages with callbacks.
Do other languages support callback of object?
You shouldn't use object methods as callbacks. Common practice in portable code is use of handles (notify calling convention):
DLL source:
type
THandle = LongWord;
{$IF SizeOf(THandle) < SizeOf(Pointer))}
{$MESSAGE Error 'Invallid handle type'}
{$ENDIF}
TCallback = procedure(const aHandle: THandle); cdecl;
var
gCallback: record
Routine: TCallback;
Obj: TObject;
Info: string
end;
function Object2Handle(const aObj: TObject): THandle;
begin
Result:= THandle(Pointer(aObj))
end;
function Handle2Object(const aHandle: THandle; out aObj: TObject): Boolean;
begin
if gCallback.Obj <> nil then
if aHandle = Object2Handle(gCallback.Obj) then
begin
aObj:= gCallback.Obj;
Result:= true;
Exit // WARRNING: program flow disorder
end;
aObj:= nil;
Result:= false
end;
procedure DoCallback();
begin
if Assigned(gCallback.Routine) then
gCallback.Routine(Object2Handle(gCallback.Obj))
end;
procedure SetupCallback(const aCallback: TCallback); cdecl;
begin
gCallback.Routine:= aCallback;
end;
procedure DoSomething(const aHandle: THandle; out aInfo: string); cdecl;
var
O: TObject;
begin
if Handle2Object(aHandle, O) then
aInfo:= Format('%s class object %s', [O.ClassName(), gCallback.Info])
end;
procedure Test();
begin
gCallback.Obj:= TStream.Create();
try
gCallback.Info:= 'created';
DoCallback();
finally
FreeAndNil(gCallback.Obj)
end;
gCallback.Obj:= TMemoryStream.Create();
try
gCallback.Info:= 'will be freed';
DoCallback();
finally
FreeAndNil(gCallback.Obj)
end
end;
exports
SetupCallback,
DoSomething,
Test;
Executable source:
procedure Cb(const aHandle: THandle); cdecl;
const
STUPID: THandle = 1;
EQUALLY_STUPID = $DEAD;
var
S: string;
begin
DoSomething(STUPID, S);
DoSomething(aHandle, S);
DoSomething(EQUALLY_STUPID, S)
end;
begin
SetupCallback(#Cb);
Test()
end.
Edited: You can't shoot yourself in you leg now.
I guess other languages have different technologies as alternative to messages?
OS have a few message alternatives. However not many truly portable.
You can also use:
sockets,
(IMO too big in this case?) ready messaging system (my favorite 0MQ)
im currently working in a simple program that implements plugins whith dll libraries (using the TJvPluginManager from the JVCL Framework).
So far I figure out how to use this component to handle commands but what if i want to have a return value from a custom function inside the library?. It is posible to call a certain function from the host by using the TJvPluginManager? How should I implement this?.
The hole idea is to have a function that returns a string inside each dll so it can be called by using a simple cicle. I think I can do this by hand (using dinamic loading) but I want to work with TJvPluginManager as much as possible.
Thank you for your time.
John Marko
The way I do it is to implement an Interface in the plugin and call it from the host e.g.
MyApp.Interfaces.pas
uses
Classes;
type
IMyPluginInterface = interface
['{C0436F76-6824-45E7-8819-414AB8F39E19}']
function ConvertToUpperCase(const Value: String): String;
end;
implmentation
end.
The plugin:
uses
..., MyApp.Interfaces;
type
TMyPluginDemo = class(TJvPlugIn, IMyPluginInterface)
public
function ConvertToUpperCase(const Value: String): String;
...
implmentation
function TMyPluginDemo.ConvertToUpperCase(const Value: String): String;
begin
Result := UpperCase(Value);
end;
...
The host:
uses
..., MyApp.Interfaces;
...
function TMyHostApp.GetPluginUpperCase(Plugin: TjvPlugin; const Value: String): String;
var
MyPluginInterface: IMyPluginInterface;
begin
if Supports(Plugin, IMyPluginInterface, MyPluginInterface) then
Result := MyPluginInterface.ConvertToUpperCase(Value)
else
raise Exception.Create('Plugin does not support IMyPluginInterface');
end;
Hope this helps.
Subj. I'd like to use strings instead of PChar because that spares me much casting, but if I just do
procedure SomeExternalProc(s: string); external SOMEDLL_DLL;
and then implement it in some other project with non-shared memory manager:
library SeparateDll;
procedure SomeExternalProc(s: string);
begin
//a bla bla bla
//code here code here
end;
I have (formally) no guarantee Delphi does not decide for whatever reason to alter the string, modify its reference counter, duplicate or unique it, or whatever else. For example
var InternalString: string;
procedure SomeExternalProc(s: string);
begin
InternalString := s;
end;
Delphi increments refcounter and copies a pointer, that's it. I'd like Delphi to copy the data. Does declaring the parameter as "const" make it safe for that reason? If not, is there a way to do it? Declaring parameter as PChar doesn't seem to be a solution because you need to cast it every time:
procedure SomeExternalProc(s: Pchar); forward;
procedure LocalProc;
var local_s: string;
begin
SomeExternalProc(local_s); //<<--- incompatible types: 'string' and 'PAnsiChar'
end;
That would probably work, as long as you only ever use your DLL from code compiled in the same version of Delphi. The internal format of string has been known to change between releases, and you have no formal guarantee that it won't change again.
If you want to avoid having to cast everywhere you use it, try wrapping the function, like this:
procedure SomeExternalProc(s: Pchar); external dllname;
procedure MyExternalProc(s: string); inline;
begin
SomeExternalProc(PChar(local_s));
end;
Then in your code, you call MyExternalProc instead of SomeExternalProc, and everyone's happy.
If both the app and the DLL are written in the same Delphi release, just use shared memory manager (more details here).
If one side is written in a different language than there's no other way but to use PChar or WideString (WideStrings are managed by the COM memory manager).
Or you can write a wrapper function:
procedure MyExternalProc(const s: string);
begin
SomeExternalProc(PChar(s));
end;
Just to add a single fact:
Delphi allows you to simply assign PChar to a string so on the DLL side you don't need any typecast:
function MyDllFunction(_s: PChar): integer;
var
s: string;
begin
s := _s; // implicit conversion to string
// now work with s instead of the _s parameter
end;
This also applies for passing PChar as a parameter to a function that expects a (by value) string.
I recommend to use an alternative memory manager such as RecyclerMM or FastMM. They doesn't require any external shared MM dll's and allows you to pass strings to the dlls safely. As a bonus, you may get a nice performance improvement in whole application.
FastMM is used as a default memory manager in Delphi 2006 and above. Also it's a good tool to search the memory-leaks.