I just converted my server to code-first and all compiles okay and mostly runs okay.
There are a few instances in my client app where I use the following code
(RORemoteService1 as IMyService).SomeMethod;
which causes the error Interface not supported
This happens at various places in the client using the same cast operation. (Explicit service creation - CoMyService.create… - appears to work okay.)
a) Does this strategy work with code-first clients? If so, how ?
b) Will this make older clients that were compiled against the RODL-first server incompatible with the new code-first server release? (That would be a huge problem for me)
I don’t think that’s the issue … the error is different (it’s not unknown method it’s “Interface not supported”)
In most cases the TRORemoteService.ServiceName is set at design-time using the drop down list.
Only in one place do I assign the service name manually:
var
fFileService : TFileService;
begin
fRORemoteService := TRORemoteService.Create(nil);
fRORemoteService.Channel := fROChannel;
fRORemoteService.Message := fROMessage;
fRORemoteService.ServiceName := 'FileService';
fFileService := fRORemoteService as IFileService; // "Interface not supported" error on this line
[....snip...]
end;
and this is my service declaration.
{ TFileService }
[ROService]
[RODocumentation('Service for transferring files between server and client')]
TFileService = class(TDataAbstractService)
DASchema1: TDASchema;
Bin2DataStreamer: TDABin2DataStreamer;
[...]
initialization
RegisterCodeFirstService(TFileService); // fake declaration for RTTI
fClassFactory := TROClassFactory.Create('FileService', {$IFDEF FPC}@{$ENDIF}Create_FileService, TRORTTIInvoker);
// RegisterForZeroConf(fClassFactory,'_FileService_rosdk._tcp.');
I have multiple services in the server, and this only happens with (so far) two of them, although I have yet to conduct extensive testing.
One of the services works fine using the (RORemoteService1 as IServiceName).methodCall code.
I’ve just realised, that my previous statement that some of the services were working was incorrect. I realise that code wasn’t being called.
In fact all attempts to cast a TRORemoteService to a service interface fails with “Interface not supported”.
Answer to your question: yes, the service names are correct. The RORemoteService components feed into a RemoteDataAdapter that in turn expose the correct schema datatables that I use in the TDAMemDataTables – so they have to be correct.
Thanks, didn’t think you would be able to as it does seem basic functionality.
If it helps, I have connected the previous client (that was built against RODL server) to the code-first server and the functions that use the (TRORemoteService as IMyService).methodCall code work fine.
So the problem must lie in the new “codefirst client” as the server looks okay?
in general, you can receive server instance via CoXXXX.Create method and use it instead receiving it via TRORemoteService.
it will be the same as (RORemoteService1 as IXXXX) but a bit faster because RORemoteService performs some additional actions like looking for proxy class in registered proxy list.
Which begs the question: what changed client side between the working and broken version. Do you still have the old and the new _Intf file, can you compare them? Did anything else change?
To me this sounds like a problem with mismatched interface GUIDs, somehow
The difference is that the server has been converted to code-first from a RODL first (via the conversion wizard - which did a good job btw).
So the xxxLibrary_Intf file has to be generated client-side using the “Connect to Remoting SDK Server” IDE menu.
I compared the interface ID’s from the RODL-first server-side to the code-first client-side generated ID’s and the Library ID was the same GUID, but the service GUIDs are different.
I pasted the old GUIDs into the new code-first unit and re-compiled, but unfortunately ran into the same problem.
Whilst I can code-around this, I’m wary that this problem could be indicative of a deeper problem with the new code-first server/client setup ?
Thanks
Edit: forgot to mention, I confirmed the problem is purely client-side because I ran the code-first client against the most recent build of the RODL first server and the same problem arose.
Yeah, it’s definitely weird. It’s almost as if the client has two separate sets of GUIDs, one on the interface types you use in code, and another that TRORemoteService uses to cast — and they don’t match.
I don’t recall the details (Eugene will know better, as its been, quite literally, a decade+ since I myself was involved with writing that code ;), but iirc TRORemoteService does some pretty low-level hacking to support the casting on Delphi/COM interface level.
I just did a check for duplicate interface declarations & the only thing grep search found was the backup made by the code-first wizard & the interface file generated client-side.
I’m stumped.
I’ll try to replicate the problem in another test client, but I shall code-around the problem in the real application.
Hopefully Eugene will have more ideas. Have you tried tracing into the cast to see whats going on?
essentially
function TRORemoteService.QueryInterface(const IID: TGUID; out Obj): HResult;
var
proxyclass : TROProxyClass;
proxy : TROProxy;
begin
result := inherited QueryInterface(IID, Obj);
if (result <> S_OK) then begin
proxyclass := FindProxyClass(IID, TRUE);
if (proxyclass=NIL) then Exit
else begin
CheckCanConnect(false);
proxy := proxyclass.Create(Self);
proxy.GetInterface(IID, Obj);
result := S_OK;
end;
end;
end;
should be where the magic happens. Check what Guid is stored in IID, and step into FindProxyClass to see which ones it knows about…
Thanks for your insights marc, it’s helped solve the problem.
I built a simple app just to login to the server and then logout using (RORemoteServer as ILoginService).Logout as I normally do & which has been failling all afternoon.
The error occurred as expected, so (as a hail-mary) I regenerated the Intf unit again. Hey presto the simple login/out app started working fine.
Recompiled the big app and that is now working too.
Based on marcs comments, I looked at the xxxLibrary_Intf file and saw that the interface IID’s are declared as constants, but the interface declarations use the same string literals, not the declared constants.
So I’m guessing that the two may have become out of sync somehow… ?
We can replicate the problem in a simple client app by altering the IxxxxxService_IID : TGuid = '{....}’ constant declaration so that it no longer matches the actual TGuid used in the interface declaration.