EIntfCastError 'Interface not supported' when run as a TServiceApplication - delphi

I'm having problems using a COM-object when I run my application as a Windoes Service, i.e. TServiceApplication. The exception EIntfCastError 'Interface not supported' is raised.
If I run the application as a normal Delphi app then it works fine, including if I run as a service using srvany.exe
type IMyInter = interface (IUnknown)
['{9E6B311E-C6D3-4687-B272-3FBE9DBC2DD6}']
//...
end;
type
TMyObject = class
private
FMyInter: IMyInter;
published
constructor Create(const ClassID: TGUID);
end;
constructor TMyObject.Create(const ClassID:TGUID);
begin
CoInitialize(nil);
FMyInter := CreateComObject(ClassID) as IMyInter;
//....
end;
It seems like the error is raised after the call to CreateComObject when the result is going to be assigned to FMyInter. Both the application and COM-object are 32-bit. I'm running on Windows 7 64bit and using Delphi XE3. The COM-object has been registered with regsvr32.exe
Any help would be appreciated

I finally managed to solve the problem which resided on the COM-server side. When creating the object, i.e. TComObjectFactory.Create I changed the threading model from tmSingle to tmApartment. Then I unregistered and re-registered the server. Presto! Not quite sure why but it works for me.
...
initialization
TComObjectFactory.Create( ComServer, TMyComServerClass, Class_ComServerClassGUID, ‘My Com Server Class’, ‘My Descriptive text’, ciMultiInstance, tmApartment);

Related

COM-object in service application cannot be accessed

I`m developing a service application with COM-object in it (OPC Data Access 2.05 server). I have this code for registration my object, which is executed after installation:
procedure TOPCService.ServiceAfterInstall(Sender: TService);
var
lcHResult: HRESULT;
lcCLSIDString: String;
begin
ComServer.UpdateRegistry(True);
lcCLSIDString:=GUIDToString(CLASS_TestOPCServerDA2);
ComObj.CreateRegKey('AppID\'+lcCLSIDString, '', 'Test OPC Server DA2');
ComObj.CreateRegKey('AppID\'+Application.ExeName, 'AppId', lcCLSIDString);
ComObj.CreateRegKey('CLSID\'+lcCLSIDString+'\VersionIndependentProgID', '', C_TEST_OPC_DA2_SERVER_NAME);
<opc server registration stuff>
RegisterAsService(lcCLSIDString, Name);
end;
The service and COM-object are properly register in the system, so i can see my service in SCM and COM-object in OLE/COM object viewer (and also in OPC clients).
The COM-object itself looks like this:
type
TTestOPCServerDA2 = class(TAutoObject, ITestOPCServerDA2, IConnectionPointContainer, IOPCServer, IOPCCommon, IOPCItemProperties, IOPCBrowseServerAddressSpace)
with its factory registration code:
initialization
TAutoObjectFactory.Create(ComServer, TTestOPCServerDA2, Class_TestOPCServerDA2, ciMultiInstance, tmApartment);
The problem is when i try to CoCreateInstance(CLASS_TestOPCServerDA2) (via CreateComObject wrapper), i got freeze for 120 second and 0x80080005 (CO_E_SERVER_EXEC_FAILURE) error after. In SCM and Task Manager i see my service is started when COM-object is requested, but nothing else happens. If i stop the serivce and try againg, service would be started again, so i assume Windows knows about my COM-object, its executable and the fact that executable is a service.
I also tried to change user which my service is running under (to the same with the invoking application), but that did not help.
What am i missing?
Edit 1. I created new project and got rid of OPC (just left COM support) to isolate the problem, so now my class is looks like this:
type
TTestCOMServer = class(TAutoObject, ITestCOMServer)
end;
...
initialization
TAutoObjectFactory.Create(ComServer, TTestCOMServer, Class_TestCOMServer, ciMultiInstance, tmApartment);
And the service thread:
procedure TCOMService.ServiceExecute(Sender: TService);
begin
while (not Terminated) do
begin
ReportStatus;
ServiceThread.ProcessRequests(False);
Sleep(25);
end;
The problem persists: when i try to CoCreateInstance, nothing happens and calling app hangs for 120 seconds.
But! If i make 1 change: uncommenting Application.DelayInitialize := True; in dpr, COM-object gets created well and calling app freezes no longer! Is it the service execute thread that (not service main thread) processes COM-requests?
Edit 2. It seems that only DelayInititalization is requred. ProcessRequests can be called with False argument and sleep can have its place - i must have not properly rebuilded my project.
So, i think the answer to my question is to uncomment Application.DelayInitialize := True; in DPR-file. Delphi autogenerate text about that, but it mentions only Windows 2003 Server condition and my OS is Windows 10.
In my case (Delphi XE3 under Windows 10 Pro) i had to uncomment
Application.DelayInitialize := True;
in DPR. After this change, COM-object is created properly.

Getting error as "Class not Registered"

I am trying to create an Active X DLL in delphi.
I have added a TLB file have an Interface with a CoClass declaration and I have implemented the interface in some another unit.
The DLL was successfully built and registered too.
But, when I am trying to use the DLL for creating and calling methods from the another project it is showing error as Class Not Registered.
I am trying to get some information about the same but not able to get answer.
Here is a interface implementation code for the DLL:
unit uinfComTestProject;
interface
uses
ComTestProject_TLB;
Type
TComTestProject = class(TInterfacedObject,ITestComCall)
public
procedure CreateAndShowMyData();safecall;
end;
implementation
{ TComTestProject }
uses
ufTestProjectForm;
procedure TComTestProject.CreateAndShowMyData;
var
frm: TForm1;
begin
frm := TForm1.Create(nil);
try
frm.ShowModal();
finally
frm.Free;
end;
//
end;
end.
Snapshot for the TLB file definition:
Method to create an instance of the interface.
procedure TForm1.Button1Click(Sender: TObject);
var
LCOm: ITestComCall;
begin
LCOm := CoTComTestProject.Create();// getting error at this point
try
LCOm.CreateAndShowMyData;
finally
LCOm := nil;
end;
end;
Can anyone please help me on same?
Please let me know if had made any mistake in the same.
Thanks in advance.
The error simply means that the COM class that you are registering has not been registered in the COM registry. Whilst you might have successfully registered the DLL, its self-registration did not register the COM class.
To understand why your DLL does not register the class requires knowledge of your registration code, which is unfortunately not present in the question.

Microsoft AlwaysOn failover solution and Delphi

I'm trying to make a Delphi application to work with AlwaysOn solution. I found on Google that I have to use MultiSubnetFailover=True in the connection string.
Application is compiled in Delphi XE3 and uses TADOConnection.
If I use Provider=SQLOLEDB in the connection string, application starts but it looks like MultiSubnetFailover=True has no effect.
If I use Provider=SQLNCLI11 (I found on Google that OLEDB doesn't support AlwaysOn solution and I have to use SQL Native client) I get invalid attribute when trying to open the connection.
The connection string is:
Provider=SQLOLEDB.1;Password="password here";Persist Security Info=True;User ID=sa;Initial Catalog="DB here";Data Source="SQL Instance here";MultiSubnetFailover=True
Do I have to upgrade to a newer version on Delphi to use this failover solution or is something that I'm missing in the connection string?
I am currently using XE2 with SQL Server AlwaysOn. If you read the documentation you will see that AlwaysOn resilience events will cause your database connection to fail and you need to initiate a new one.
If a SqlClient application is connected to an AlwaysOn database that
fails over, the original connection is broken and the application must
open a new connection to continue work after the failover.
I've dealt with this via the simple expedient of overriding the TAdoQuery component with my own version which retries the connection after getting a connection failure. This may not be the proper way to do this but it certainly works. What it does is override the methods invoked for opening (if the query returns a result set) or executes the SQL (otherwise) and if there is a failure due to connection loss error tries again (but only once). I have heavily tested this against AlwaysOn switch overs and it works reliably for our configuration. It will also react to any other connection loss events and hence deals with some other causes of queries failing. If you are using a component other than TAdoQuery you would need to create similar overrides for that component.
It is possible this can be dealt with in other ways but I stopped looking for alternatives once I found something that worked. You may want to tidy up the uses statement as it clearly includes some stuff that isn't needed. (Just looking at this code makes me want to go away and refactor the code duplication as well)
unit sptADOQuery;
interface
uses
Windows, Messages, SysUtils, Classes, Db, ADODB;
type
TsptADOQuery = class(TADOQuery)
protected
procedure SetActive(Value: Boolean); override;
public
function ExecSQL: Integer; // static override
published
end;
procedure Register;
implementation
uses ComObj;
procedure Register;
begin
RegisterComponents('dbGo', [TsptADOQuery]);
end;
procedure TsptADOQuery.SetActive(Value: Boolean);
begin
try
inherited SetActive(Value);
except
on e: EOleException do
begin
if (EOleException(e).ErrorCode = HRESULT($80004005)) then
begin
if Assigned(Connection) then
begin
Connection.Close;
Connection.Open;
end;
inherited SetActive(Value); // try again
end
else raise;
end
else raise;
end;
end;
function TsptADOQuery.ExecSQL: Integer;
begin
try
Result := inherited ExecSQL;
except
on e: EOleException do
begin
if (EOleException(e).ErrorCode = HRESULT($80004005)) then
begin
if Assigned(Connection) then
begin
Connection.Close;
Connection.Open;
end;
Result := inherited ExecSQL; // try again
end
else raise;
end
else raise;
end;
end;
end.

How can I make ADO database connections in 'TISAPIApplication` before processing incoming requests?

TADOConnection is failing to connect in the application initialization section of Delphi ISAPI App (TISAPIApplication):
Application is built with Delphi XE SPI, running Win 7 64/IIS 7.5 and WinServer 2008 RS2 - it cannot connect with ADO in the global ISAPI application context. (Example code is using MS-SQLServer OLEDB - but we also fail using Sybase ASE provider.)
The following code fails when conn.Open is called - TADOConnection.open never returns - ISAPI app hangs in la-la land, no exception raised:
library ISAPIBareBones;
uses
ActiveX,
ADODB,
(...)
var
conn: TADOConnection;
begin
CoInitFlags := COINIT_MULTITHREADED;
Application.Initialize;
coinitialize(nil);
conn := TADOConnection.Create(Application);
conn.ConnectionString := 'Provider=SQLOLEDB.1;xxx';
//Fails here:
try
conn.Open;
except on e:exception do
logException(e)
end;
Application.WebModuleClass := WebModuleClass;
Application.Run;
end.
The same code within a specific request handler (Delphi webAction) runs fine.
We suspect a problem with execution privileges in IIS at the ISAPI application level. But as far as we can tell, the entire IIS application stack from the webServer itself down to the specific virtual directory and the ISAPI dll itself are all running under the same credentials with same execution privileges.
Meanwhile, my workaround has been to initialize the database infrastructure from within an http response call (an ISAPI thread), and then simply check that it's initialized on each subsequent call. This works, but encumbers me with some constraints that I'd prefer not to deal with.
How can I make ADO database connections in a TISAPIApplication instance, before handling incoming requests.
The begin ... end or an initialization part of a dll is the Delphi equivalent to dllmain in C++. So the same restrictions apply including:
Don't call CoInitialize
Don't call COM functions
This implies that you cannot create an ADO connection.
Do you know all the things that happen when you call TADOConnection.Create(Application);?
So what you're trying to do isn't going to work. And even if it did, you should not do it. Here's some better explanations:
http://msdn.microsoft.com/en-us/library/ms682583%28VS.85%29.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/dn633971(v=vs.85).aspx
http://blogs.msdn.com/b/oldnewthing/archive/2004/01/27/63401.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2004/01/28/63880.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2014/08/21/10551659.aspx
MSDN suggests creating the database connection in GetExtensionVersion. This is how your isapi dll is initialized. It is not just for reporting the extension version. So that is the way to go. Create your own GetExtensionVersion function that initializes your database and then call the previous Delphi function.
library Project1;
uses
Winapi.ActiveX,
System.Win.ComObj,
Web.WebBroker,
Web.Win.ISAPIApp,
Web.Win.ISAPIThreadPool,
Winapi.Isapi2,
Winapi.Windows,
WebModuleUnit1 in 'WebModuleUnit1.pas' {WebModule1: TWebModule};
{$R *.res}
function GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL; stdcall;
begin
Result := Web.Win.ISAPIApp.GetExtensionVersion(Ver);
// create your ado connection here
end;
exports
GetExtensionVersion,
HttpExtensionProc,
TerminateExtension;
begin
CoInitFlags := COINIT_MULTITHREADED;
Application.Initialize;
Application.WebModuleClass := WebModuleClass;
Application.Run;
end.
I think the problem is that the code you run runs in the dll's main procedure. This part of the initialization is very restrictive e.g. you may not load any dll nor you are
allowed to call CoInitialize (see http://msdn.microsoft.com/en-us/library/ms678543%28v=vs.85%29.aspx) .
Your ActiveX alls there will cause some troubles which are most likely the reason for your
exception.

Connecting to Firebird embedded on D2010

I downloaded the Firebird DBX driver from http://sites.google.com/site/dbxfirebird/ and I've been able to compile the "Test Connection" project and get it to run. I pointed it to my test DB like so:
procedure TMainForm.Button1Click(Sender: TObject);
var C: TSQLConnection;
begin
C := TSQLConnection.Create(Self);
try
C.DriverName := 'FirebirdConnection';
C.Params.Add('User_Name=SYSDBA');
C.Params.Add('Password=masterkey');
C.Params.Add('Database=C:\fbtest\test.fdb');
C.Open;
if C.Connected then
ShowMessage('Connection is active')
finally
C.Free;
end;
end;
When I run it, it works fine. But when I put that exact same code in a different project, it doesn't work. I've copied the fbclient.dll (Firebird embedded driver DLL, renamed to fbclient), all of its dependencies, and the dbxdrivers.ini file to the same folder as the project's EXE is running in. I can't see any reason why this shouldn't work, but the call to .Open fails with:
Project Project1.exe raised exception
class TDBXError with message 'Unknown
driver: FirebirdConnection'.
Again, this is on the call to Open. The assignment to DriverName works just fine. Has anyone seen this problem before? Why does the exact same code work in the test project but not a different one, and is there any way I can fix it?
I found the problem. A loading class to set up the database driver had to be registered in the initialization section of DBXDynalink.pas. The test project included DBXDynalink in its uses clause, where mine didn't. I put that in and now it works.
This error generally occurs when you don't add the respective DBX driver unit to your uses list. Try adding DBXFirebird to your uses list.
Just change
C.DriverName := 'FirebirdConnection';
to
C.DriverName := 'Firebird';
and will work!

Resources