How to manually invoke DSProxyGEN.EXE to generate Delphi DataSnap proxy client? - delphi

I am using DataSnap of Delphi 2010. There is a DSProxyGen.EXE utility to allow us generate proxy client unit. Does anyone how to consume this utility in command line to generate the proxy client without using TSQLConnection's context menu?

DSProxyGen doesn't give you its parameters when run at the command line and doesn't respond to
DSProxyGen /?
You could try replacing it with your own exe (make a backup!) that detects what command line parameters are sent (global CmdLine variable) and write them to a file. Launch this from TSQLConnection's context menu and you should have what you need to call DSProxyGen from the command line.
Update:
From Delphi XE to XE7, source code for DSProxyGen is included as one of the samples
Samples\Delphi\DataSnap\ProxyGenerator
In Delphi XE8, it is shipped as part of the source code.
source\data\datasnap\proxygen

DSProxyGen.exe without arguments connects using TCP/IP port 211 and uses the first command-line argument as filename.
DSProxyGen test.pas
Will generate test.pas
I've found no way to specify ConnectionString options on the command-line, yet...

It is possible with the DSProxyGen.exe from Delphi XE and later, for example:
DSProxyGen.exe Proxy.pas
-C:host=localhost;port=8080;communicationprotocol=http
Run DSProxyGen.exe without parameters for usage help. (/? or /h etc. does not work)
I've noticed the source is also included in Delphi XE7:
C:\Program Files (x86)\Embarcadero\Studio\15.0\source\data\datasnap\proxygen\DSProxyGen.dproj
Note that it would not work if you have specified any Transport Filters (ie. ZLibCompression,PC1,RSA etc.), returning errors for example "TDBXError: ZLibCompression Filter not found."
A temp workaround I've used during development time is to add another TDSTCPServerTransport comp that does not have any Transport Filters and then DSProxyGen works fine.

See the unit DSProxyDesigner.dcu (for which the source file is not included, but should be at database\external\src\pas\datasnap\proxy\DSProxyDesigner.pas). This is the unit used at design-time to generate the proxy client classes. I'm not sure DSProxyGen.exe is actually used, and it's a shame there is no documentation on its command-line parameters, yet...

Related

How to have multiple versions of a Delphi COM Server at the same time?

We have a COM server written in Delphi 10.2 Tokyo which contains 4 classes, all of which inherit from TAutoObject, and all have an initialization section containing the call to TAutoObjectFactory.Create.
In a typical installation, we have approximately 60 other programs that make use of the classes in the COM server. This all works fine. Each of the classes has their own GUID and the Delphi COM server does its usual thing of ensuring they are all registered correctly as part of the InitComServer process that runs from Application.Initialize.
We now have a need to run multiple installations side-by-side, where they may be different versions. Since we don't control all of the programs, we can't produce a different version of the COM server with different GUID's for every installation.
I had found this Microsoft document:
DLL/COM Redirection on Windows
But that approach does not work. When the main program starts and creates an instance of the first COM server class, I can see that it is the registered EXE that is running, not the COM server EXE in the same folder as the main program. This might be because it's an EXE and not a DLL, and Delphi's initialization is circumventing the DLL/COM redirection.
Here's an example of using one of the COM classes:
function ProduceReport(const ATenantID, AReportID: Integer; const AFilter: String): String;
var
AReportServer: IReportServer;
begin
Result := '';
AReportServer := CoReportServer.Create;
try
if AReportServer.Connect(ATenantID) then
begin
if not AReportServer.Print(AReportID, AFilter) then
Result := AReportServer.GetLastError;
end
else
Result := AReportServer.GetLastError;
except on E: Exception do
Result := E.Message;
end;
AReportServer := nil;
end;
The main thing of note is that the CoReportServer.Create does this internally:
Result := CreateComObject(CLASS_ReportServer) as IReportServer;
which in turn is doing this:
OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IUnknown, Result));
I can't see what CoCreateInstance is doing, but I am wondering if there is a way to call CoGetInstanceFromFile instead? Perhaps by changing the CoReportServer.Create in the TLB to use my own code instead of the CreateComObject?
Would changing the call to TAutoObjectFactory.Create to use a different TClassInstancing, such as ciSingleInstance, make any difference?
I just want it so that when a program calls CoReportServer.Create, it creates an instance of the server from the EXE it has in the program's folder, not by looking up the InprocServer from the Registry. I don't want the Registry to be used at all.
[Edit]
Since we also have an OCX that will need to be registry-free, I followed the steps discussed in this post:
Generate manifest files for registration-free COM
I have a manifest file for the OCX test app and a manifest file the OCX itself, both of which are in the same folder as the test app and the OCX. The manifest file specifies the same version number of the OCX as the one in the folder.
I then copied an old version of the OCX to a different folder, ran an elevated CMD prompt, and used Regsvr32 to register that OCX in that folder.
Running the OCX test app, which includes the ActiveX control on the main form, I can see from Process Explorer that it is using the registered OCX, and not the OCX in the same folder.
This may indicate that Delphi EXE programs can't handle the manifest approach to this, even if I include the custom manifest in the Project Options.
We have a mix of COM server EXE, ActiveX visual control OCX, and ActiveX type library DLL, all written in Delphi 10.2 Tokyo, that we need to make registry-free.
[Edit 23/03/2022]
I've made some progress on this by referencing the information contained in the answer for Registration-free COM/DLL?
I now have a manifest file for the DLL containing an <assemblyIdentity> tag, a <file> tag containing a list of <comClass> tags for each CLSID in my DLL, and a list of <comInterfaceExternalProxyStub> tags to link the CLSID's and IID's with the interface name.
And there is a manifest file for the host EXE, that includes the same <assemblyIdentity> tag information, but contained within a <dependentAssembly> tag.
Using SxSTrace, I can see "INFO: Activation Context generation succeeded.", so at least the side-by-side configuration is correct.
However, when running the test program, at the point it is calling CoReportServer.Create, it is falling over with a "Error loading type library/DLL" message. Based on the stack trace, this is from the LoadTypeLibrary call inside the TAutoObjectFactory.Create process from the initialization section of the first class implementation of the first interface.
Since the SO answer I looked at mentions making sure it will work when the DLL is registered, I tried using regsvr32 from an elevated command prompt, but get a "Runtime error 217" popup, followed by "The module "reportserver.dll" failed to load. A dynamic link library (DLL) initialization routine failed."
The Result from LoadTypeLibEx is -2147312566, which doesn't seem to match the TYPE_E_CANTLOADLIBRARY result.
So it looks like I may have got the registry-free COM part to work, but the conversion of the out-of-process COM server to an in-process COM library isn't right.
So I went back to the COM server EXE, and re-registered that to confirm the test program still works. It did. Then I altered the test program manifest to point to the COM server EXE instead of my trial conversion DLL, made a new manifest file for the COM server EXE, containing all the IID's and CLSID's, and un-registered the EXE.
The test program reports "Class not registered", so that pretty much confirms the activation context approach only works on DLL's.
That leaves me with figuring out why the DLL will not load, although another issue I just thought of is that the DLL is in-process, meaning every program that uses the library has to create it's own instance. That could be very resource intensive.
[Edit 25/03/2022]
Skipping the out-of-process COM server for the moment, I've now taken a look at our ActiveX Control, which is a descendent of a TProgressBar and is used either on a VCL form or created at runtime.
Following the same approach for defining the manifest file, I've created the <assemblyIdentify>, <file> and <comInterfaceExternalProxyStub> tags, but I noticed that in the _TLB.pas, there is one extra TGUID for DIID_IReportControlEvents.
Reviewing the Microsoft documentation for manifest files, I cannot see any reference to an events type of interface, so I'm not sure how that one will work.
In any case, a manifest file doesn't work for an OCX. I just get the "Class not registered" error.
The SxStrace file shows "INFO: Activation Context generation succeeded." but it doesn't include any information about the manifest for the OCX, so it is not loading it at all.
This is probably because the _TLB.pas for the OCX shows it is using OleControl rather than COM. Removing the {$E ocx} from the DPR means Delphi creates a DLL instead, but that also fails.

Error: F1026 File not found: 'System.Actions.dcu' by Command Line Compiler

The editor automatically adds the System.Actions unit when one of my forms is saved.
It compiles without a problem in the IDE, but unfortunately the Command Line Compiler can not find the file and gives me the error:
Error: F1026 File not found: 'System.Actions.dcu'
What am I missing?
In what follows, I am assuming that you are using msbuild to compile your program.
The System.Actions unit was added in XE3 to support actions in both FMX and VCL frameworks. Prior to that release FMX had no actions. Some classes were moved around, out of VCL units and into the new System.Actions unit which can be used by both FMX and VCL code.
So, the compiler error that you see suggests to me that you are unintentionally compiling with a version that pre-dates this change. My guess is that your IDE is XE3 or later, but that your command line build is configured to use an earlier version of Delphi. Most likely through the PATH environment variable, and whatever Embarcadero entry happens to be first in that variable.
If my hunch is correct then you need to ensure that you compile with the desired version.
The way I organise machines that have multiple Delphi installations is as follows:
Remove all Embarcadero entries from your PATH environment variable.
Whenever you need to build at the command line, configure the environment, for instance by running the appropriate rsvars.bat script (found in the bin directory of your Delphi installation) before you call msbuild.
This way you cannot accidentally find the wrong version because you have to explicitly configure an environment.
On the other hand, perhaps you are calling dcc32 directly. Don't do that. You will have to supply all the options and paths that are already defined in your project file. Repeating that is just a way to create a maintenence headache and make it likely that your command line build won't match the IDE build.
Instead, use msbuild to build your program. That way you can use the settings defined in your project file.
Thanks Hefferman for your advice but we shall stick with dcc32. It's easier to customize. For example we didn't figure out how to use more than one 'define' parameter with msbuild. It's possible to use dcc32 with the -NS switch for dependent 'uses' and that is our solution. We also create some .dpr files with code and in that case we do not have a corresponding .dproj file.

Registering OLE server in Delphi XE5 and non-pascal codes in ridl file

I am writing an exe OLE server to embed it in my own application. I am running OLE server (exe) with /regserver parameter as a normal user and I am getting following error:
Error accessing the OLE registry
I don't need any other programs to use this server. It is just for my own client and it will not be a DLL. Is there a way to register it as standard user?
Other problem is that i see codes which are not pascal in ridl file. Types of properties are C not Pascal. For example i see DATE but i don't see TDateTime in the list. I am using XE5.
Use switch "/REGSERVERPERUSER"
I use XE5, it work.
Delphi write corresponding register keys
Use the PerUserRegistration to ensure that your COM server self-registers to the per-user HKCU hive. Or simply write the registry settings yourself. Which is the recommended approach anyway if you are writing an install program.
RIDL is a flavour of Interface Description Language (IDL). It is used to describe the interface of your COM server. It is not Pascal. It's not passed to a Pascal compiler. It's processed by a tool that understands RIDL. Everything is as expected.

how to debug a 64 bit dll registration process made with regsvr32.exe?

i have
Run time Error 216 at ADDRESS
when registering a 64 bit dll built with Delphi XE2 (I have Update 3).
from command prompt I do (note: system32 folder contains the 64bit exe!)
c:\windows\system32\regsvr32.exe My64bitdll.dll
and after a "dll succesfully installed message"
i have the runtime error.
I would like to debug the registering process, somehow using Run/Parameters/host.
Could anyone post the correct procedure? In some other questions like this one a bug is mentioned, but it seems fixed now, i have a delphi build older than this one.
Update:
Also any comment on the RunTime error is welcome.
Load the DLL project.
Modify run parameters (Run | Parameters) to specify host app as regsvr32. Note that you may need to use C:\Windows\sysnative path to defeat the 32 bit file system redirector.
Include path to DLL as command line arguments.
Perhaps enable Debug DCUs, in case the error is raised in the Delphi COM self-registration code.
Then debug the DLL like any other DLL.
Runtime error 216 is an access violation.
1) this runtime error may be just program exit. For example would you debug internals of DllMain, you can easily get past exit point and try to trace ended DLL, which would through RE. Process exit is not looking just like return from subroutine - but rather as a call to special system API function. But debugger does not understand it and continues to trace now dead project.
2) i see not point in using RegSvr32.exe or TRegSvr.exe for debuging. All RegSvr32 does is calling predefined function from DLL. Do you debug your DLL or RegSvr32 itself ?
2.1) If latter - i heard there are sources of RegSvr32, and probably there are debug symbols, but some Microsoft debugger to be used.
2.2) If former, then there should be now difference how to call those functions and you only have to debug those functions. Just take any code from File not found when registering DLL with TFileRun and regsvr32 and use it as host.

Can Delphi 2007 IDE use Delphi 2010's cgrc.exe while compile RC files?

The CGRC.exe in Delphi 2010 support using dot as resource name. I wish to use it in Delphi 2007 IDE as well. Is that possible to do so?
In Delphi 2007 IDE, when we compile a project that has .RC file, the build message will show something like:
BRCC Command Line
-w32 -foC:\Users\coder\Project\account.core.res -iC:\Users\coder\Project\developer -dDEBUG C:\Users\coder\Project\account.core.rc
However, it doesn't really use BRCC32.EXE to compile the RC files as I rename the
C:\Program Files (x86)\CodeGear\RAD Studio\5.0\bin\brcc32.exe
to something else.
Delphi 2007 evidently doesn't give you an option of what to run. If it already gave you an option to directly run RC, then it would provide you no benefit to use CGRC instead. The documentation tells you that CGRC does nothing but translate the BRCC command-line syntax into the syntax that Microsoft's RC requires, and then it runs RC.
The utility is probably provided so that the IDE only needs one command-line-generating subroutine instead of two or more. With this utility, the IDE can generate a single set of command-line options, so it only has to vary the EXE file it invokes based on the project options.
It seems that you were hinting at the possibility of replacing the brcc32.exe file that Delphi 2007 provides with the cgrc.exe file Delphi 2010 provides. But Delphi doesn't actually run brcc32.exe, apparently. That suggests that the actual resource-compilation ability is in a DLL that's shared by both brcc32.exe and the IDE or the code compiler. You don't want to go down the path of replacing DLLs.
So I guess the answer to your question is no. You'll have to find some other way of achieving whatever it is you're try to do.
In Delphi 2007 the only way I found is to run RC.EXE or in a pre-build script or in a build tool script (I use CCNet).

Resources