INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Jobs

CoInitialize has not been called

CoInitialize has not been called

(OP)
Hi all,

Delphi XE6, and testing a new service application. Getting an EOleSysError exception "CoInitialize has not been called" when trying to connect to a Jet database. I'm using TADOConnection.

Except... I am calling it. Logging shows that CoInitialize(nil) gets called before the exception occurs. I've tried putting it in all sorts of places including in the TService.Create method before anything else happens. Still get the exception.

It's doubly weird because I realised that other similarly structured service applications (Delphi XE6, Jet, ADO) I've written recently are working fine, and I haven't called CoInitialize in them at all.

Feels like something weird is going on, eg. I haven't included a magic unit in my uses clause or something.

Thanks for your help.

RE: CoInitialize has not been called

(OP)
More info to answer any questions:
  • The service application is single threaded. Functionality is gained through a TTimer event.
  • I added code to check the result of the (only) CoInitialize I've added to the project - it returns S_FALSE, indicating CoInitialize has already been called. This is consistent with my other service application projects where I haven't had to worry about CoInitialize at all.
  • I did a search in every *.pas file in my project folder, and my library folder looking for any custom unit that may be calling CoInitialize - none found aside from some Project JEDI units which I'm definitely not using in this project.

RE: CoInitialize has not been called

Not sure, but I think your problem my be related to the use of a TTimer within a service application. TTimer relies on a message pump to receive a WM_TIMER message. A normal VCL application sets up a message pump for you, but a console application or Service application do not. If you still want to develop your service as a single thread, then I suggest follow the instructions here.

Specifically:

CODE

procedure TCompanySqlDatabaseSpecialSomething.ServiceExecute(Sender: TService);
const
  SecBetweenRuns = 10;
var
  Count: Integer;
begin
  Count := 0;
  while not Terminated do
  begin
    Inc(Count);
    if Count >= SecBetweenRuns then //simulates timer event every 10 seconds.
    begin
      Count := 0;

      { place your service code here }
      { this is where the action happens }
      SomeProcedureInAnotherUnit;

    end;
    Sleep(1000); //now no timer is needed...the service sleeps for 1 sec 
    ServiceThread.ProcessRequests(False);
  end;
end; 

Still make sure you call CoInitialize(nil) before and CoUninitialize after using ADO components, or better, in the initialization and finalization sections of your service.

RE: CoInitialize has not been called

It is simple really, make sure you are calling CoInitialize/CoUnItialize in the same context (thread).

Maljumbo, your suggestion of calling CoInitialize in the initialization section is wrong since it will be called on a different thread.

to expand your example:

CODE

procedure TCompanySqlDatabaseSpecialSomething.ServiceExecute(Sender: TService);
const
  SecBetweenRuns = 10;
var
  Count: Integer;
begin
 CoInitialize(nil);
 try 
  // now can act on COM objects (like the ADO components)
  Count := 0;
  while not Terminated do
  begin
    Inc(Count);
    if Count >= SecBetweenRuns then //simulates timer event every 10 seconds.
    begin
      Count := 0;

      { place your service code here }
      { this is where the action happens }
      SomeProcedureInAnotherUnit;

    end;
    Sleep(1000); //now no timer is needed...the service sleeps for 1 sec 
    ServiceThread.ProcessRequests(False);
  end;
 finally 
  CoUninitialize;
 end;
end; 

/Daddy

-----------------------------------------------------
Helping people is my job...

RE: CoInitialize has not been called

(OP)
Hi guys,

The TTimer is a red herring - the exception is occuring in the TService.OnStart event - I haven't even created the TTimer object yet. One of the first things to happen is open the connection to the MsAccess database to enable logging (fail over logging is to the system event log, which is how I know what is happening).

whorsdaddy - I've tried calling CoInitialize(nil) in the statement just prior to creating the TADOConnection object, as well as in the TService.OnCreate event. This service application doesn't create any additional threads.

I'll try and make a new project without any libraries and just copying the relevant code lines into the OnStart event and see if I can get it to fail with CoInitialize(nil) being called at the beginning.

RE: CoInitialize has not been called

The problem is that TService automatically creates a new TServiceThread on its own when it starts. Try moving all your ADO startup code into the TService.OnStart event along with the CoInitialize statement.

RE: CoInitialize has not been called

(OP)
Ok. I've copied bits of code out of my framework library into a single unit that compiles and still faults. You can test the code below by creating a new Service Application in Delphi, then replacing the main unit with everything below, and attaching the 5 events listed in TService1 using the Object Inspector.

Compile, install and Start to test. It creates a log file in the same folder with what each Start has performed.

You can change the ForceFault constant at the beginning of the implementation section to either fault or not to show the weirdness that's happening.

TServiceMate works by pointing the TService events to it's own to handle automatic logging and logic class handling. When used, TService1.ServiceStart and TService1.ServiceStop aren't called.

As you can see, there's no threads, or other objects being created aside from what's right here.

With ForceFault=True, the FConnection.Open causes an exception because CoInitialize hasn't been called (even though you can see it has, and the log shows it has tried, although the return result shows that it had already been called?). With ForceFault=False, TServiceMate is bypassed, and FConnection.Open works fine, and CoInitialize returns S_OK, rather than S_FALSE.

None of it makes any sense to me. Using Delphi XE6 Update 1.

CODE

unit mainsvc;

interface

uses
  Winapi.Windows, System.SysUtils, Vcl.SvcMgr, ADODB;

type
  TLogic = class
  private
    FConnection: TADOConnection;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Initialise;
  end;

  TServiceMate = class
  private
    FAppLogic: TLogic;
    FDescription: String;
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
  public
    constructor Create(AService: TService; ADescription: String);
    destructor Destroy; override;
    property AppLogic: TLogic read FAppLogic write FAppLogic;
  end;

  TService1 = class(TService)
    procedure ServiceCreate(Sender: TObject);
    procedure ServiceDestroy(Sender: TObject);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServiceExecute(Sender: TService);
  private
    FLogic: TLogic;
    FSvcMate: TServiceMate;
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;

  procedure Log(AMessage: String);

var
  Service1: TService1;

implementation

uses
 ActiveX;

const
  ForceFault = True;

var
  GLogFile: String;

{$R *.DFM}

procedure Log(AMessage: String);

var
  t : TextFile;
begin
  AssignFile(t, GLogFile);
  if FileExists(GLogFile) then
    Append(t)
  else
    Rewrite(t);
  try
    WriteLn(t, DateTimeToStr(Now) + ': ' + AMessage);
  finally
    CloseFile(t);
  end;
end;

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TService1.ServiceCreate(Sender: TObject);
begin
  GLogFile := ChangeFileExt(ParamStr(0), '.log');
  DeleteFile(GLogFile);
  if ForceFault then
  begin
    FSvcMate := TServiceMate.Create(Self, 'Test CoInitialise service');
    FSvcMate.AppLogic := TLogic.Create;
  end;
end;

procedure TService1.ServiceDestroy(Sender: TObject);
begin
  FSvcMate.Free;
end;

procedure TService1.ServiceExecute(Sender: TService);
begin
  Log('ServiceExecute entered');
  with Sender do
    while not Terminated do
      ServiceThread.ProcessRequests(True);
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  FLogic := TLogic.Create;
  FLogic.Initialise;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  FLogic.Free;
end;

{ TLogic }

constructor TLogic.Create;
begin
  inherited;
  case CoInitialize(nil) of
    E_INVALIDARG : Log('CoInitialize failed: E_INVALIDARG');
    E_OUTOFMEMORY: Log('CoInitialize failed: E_OUTOFMEMORY');
    E_UNEXPECTED: Log('CoInitialize failed: E_UNEXPECTED');
    S_OK: Log('CoInitialize completed successfully');
    S_FALSE: Log('CoInitialize already called');
    RPC_E_CHANGED_MODE: Log('CoInitialize already called with different concurrency model');
  else
    Log('Unknown response from CoInitialize');
  end;
  FConnection := TADOConnection.Create(nil);
end;

destructor TLogic.Destroy;
begin
  FConnection.Free;
  CoUninitialize;
  inherited;
end;

procedure TLogic.Initialise;
const
  DBName = 'c:\test.mdb';
begin
  FConnection.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source='
    + DBName + ';Persist Security Info=False;';
  FConnection.LoginPrompt := False;
  try
    Log('About to connect to DB');
    FConnection.Open;
    Log('Connected ok');
  except
    on E: Exception do
    begin
      Log(Format('(%s) %s', [E.ClassName, E.Message]));
      raise;
    end;
  end;
end;

{ TServiceMate }

constructor TServiceMate.Create(AService: TService; ADescription: String);
begin
  Log('ServiceMate.Create entered');
  inherited Create;
  Log('ServiceMate.Create inherited');
  FDescription := ADescription;
  if Assigned(AService) then
  begin
    AService.OnStart := ServiceStart;
    AService.OnStop := ServiceStop;
    Log('ServiceMate.Create adding service');
  end;
  FAppLogic := nil;
  Log('ServiceMate.Create complete');
end;

destructor TServiceMate.Destroy;
begin
  FreeAndNil(FAppLogic);
  inherited;
end;

procedure TServiceMate.ServiceStart(Sender: TService; var Started: Boolean);
begin
  Log('ServiceMate.ServiceStart entered');
  if Assigned(FAppLogic) then
  try
    Log('Initialising logic');
    FAppLogic.Initialise;
    Log('Service started');
    Started := True;
  except
    on E: Exception do
    begin
      Log(Format('(%s) %s', [E.ClassName, E.Message]));
      Started := False;
    end;
  end;
end;

procedure TServiceMate.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
end;

end. 




RE: CoInitialize has not been called

(OP)
Hi Prattaratt,

I saw your comment after I'd posted my code - and can confirm you're correct. By moving all the code from TLogic.Create to TLogic.Initialise, it doesn't fault. Vice versa, moving TLogic.Create from TService1.ServiceStart to TService1.ServiceCreate (as an else to ForceFault), it will always fault.

Finally it makes sense. Thank you!

RE: CoInitialize has not been called

Griffyn,

did you see my code?
The execute block will run in a different thread context as you found out.
Keep all COM related code in the same thread and you are good to go...

/Daddy

-----------------------------------------------------
Helping people is my job...

RE: CoInitialize has not been called

(OP)
Hi whosrdaddy,

I did see your code, but I didn't realise that TService.Create and TService.Start run in different threads, so when I first read through it I figured it didn't really apply. It would have solved my problem of course. My TService.Execute events never contain more than the service thread loop shown in my code above.

I know you have extensive experience with service applications, and the framework I'm developing has tried to adhere to all the good points you've given out over the years. Would you recommend I abandon the TService.Create and TService.Start events and have everything execute in TService.Execute?

RE: CoInitialize has not been called

The point is that you don't need to implement TService.Execute.
Just use the Start and Stop events to fire up your worker thread.
Execute the inialization block right at the beginning of the worker thread Execute method and finalize and the end of this method.
Doing so will have multiple advantages:
- you remove the dependency on the service application, this means that you can test easily (create a forms app and create your workerthread)
- you will never run into problems like you saw with CoInitialize and stuff

My service applications are always 2 in one :
- a forms application
- a service application

depending on a startup parameter I can start the forms application which is used to configure the service.

I will see if I can post the barebone bits here...

/Daddy

-----------------------------------------------------
Helping people is my job...

Red Flag This Post

Please let us know here why this post is inappropriate. Reasons such as off-topic, duplicates, flames, illegal, vulgar, or students posting their homework.

Red Flag Submitted

Thank you for helping keep Tek-Tips Forums free from inappropriate posts.
The Tek-Tips staff will check this out and take appropriate action.

Reply To This Thread

Posting in the Tek-Tips forums is a member-only feature.

Click Here to join Tek-Tips and talk with other members!

Resources

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close