-1

I've created a new DUnit Test Project and I am trying to setup multithreaded apartment at its start. The problem is that on one computer apartment type is changed.

program COMApartment;

{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}

uses
  Winapi.ActiveX,
  TestuApartmentInfo in 'TestuApartmentInfo.pas',
  DUnitTestRunner;

{R *.RES}

begin
  CoUninitialize;
  CoInitializeEx(nil, COINIT_MULTITHREADED); // Result is S_OK
  Log(GetCurrentApartmentType); //APTTYPE_MTA on both computers.
  DUnitTestRunner.RunRegisteredTests;
end.

Now, when I run this simple test:

unit TestuApartmentInfo;

interface

uses
  TestFramework, Winapi.Windows, uApartmentInfo;

type
  TestIComThreadingInfo = class(TTestCase)
  published
    procedure ApartmentType;
  end;

implementation

uses
  Dialogs, System.SysUtils;

procedure TestIComThreadingInfo.ApartmentType;
begin
  //This gives APTTYPE_MTA on my dev computer (Windows 7) and APTTYPE_MAINSTA or APTTYPE_STA on virtual machine (Windows 2007 Server).
  Log(GetCurrentApartmentType); 
end;

initialization
  RegisterTest(TestIComThreadingInfo.Suite);
end.

I don't understand different behaviour on different computers. Is this due to different operating systems? In my test I could spawn another thread and specify apartment model for it and it would work, but my curious nature wants to know why different results in above case.

GetCurrentApartmentType has been implemented as in this article and it works properly. This is a sample application to illustrate problem which I have with some COM objects that need to run in multithreaded apartment model.

Wodzu
  • 6,932
  • 10
  • 65
  • 105
  • 1
    Are you aware that `CoUninitialize` doesn't always completely uninitialize COM? Within a single thread, you can safely call `CoInitialize` multiple times with the same apartment. Each call increments a count. Each call to `CoUninitialize` decrements that count. When the count reaches 0, the apartment is destroyed. My guess is that the framework that you're using puts an additional +1 on the count on your Windows 2007 server. I don't know why that might be, however. – Michael Gunter Mar 13 '17 at 16:04
  • To test this theory, you can use [`CoRegisterInitializeSpy`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms679753.aspx) to register an [`IInitializeSpy`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms690179.aspx) instance (you would have to create your own implementation to do this). There, you can see the inits/uninits and their counts. – Michael Gunter Mar 13 '17 at 16:08
  • Why do you call CoUninitialize at all? What do you imagine registered already? – David Heffernan Mar 13 '17 at 20:04
  • 3
    That call to CoUninitialize is a massive Red Fag. If you find yourself calling CoUninitialize to undo someone else's CoInitialize[Ex], you're kind of pulling - not the rug but the whole floor, from under their feet. Don't expect that whatever called CoInitialize is going to take it kindly and work correctly; in fact, it probably won't work at all. – Euro Micelli Mar 14 '17 at 02:34
  • @DavidHeffernan If I do not call CoUninitialize then subsequent call to CoInitializeEx won't make effect and the apartment model is `APTTYPE_MAINSTA` – Wodzu Mar 14 '17 at 07:27
  • You have the wrong solution. Don't initialize COM in the first place!! Something else is doing that. Find out what. You may need to put this other code in a separate thread if you can't wrest back control. – David Heffernan Mar 14 '17 at 07:32
  • @EuroMicelli Generally I agree but I don't think that is the case, I think Delphi by default is working in STA mode and it is nothing wrong to change it, if this is a first line of code. – Wodzu Mar 14 '17 at 08:14
  • @MichaelGunter thanks for the tip! it might come handy in a future. – Wodzu Mar 14 '17 at 08:29
  • Are you absolutely sure that this pattern is valid in Delphi? Unless it's specifically documented that way (and that would be a bizarre design), you are creating yourself a major problem. I don't know enough about Delphi, but the Delphi runtime is also a "someone else you pull the rug from under". If it calls CoInitialize on the program main thread, I would have to think that either a) the main thread must run as a STA for the runtime to work correctly, or b) there has to be a mechanism other than "undo" to switch the startup threading model. – Euro Micelli Mar 14 '17 at 12:24
  • @EuroMicelli No, Wodzu is confused here. A default VCL application is initialized as STA threaded but you can override that. But what is presented in the question isn't a VCL application. Clearly the problem is that something was erroneously initializing COM, but what that is cannot be determined from the code presented. – David Heffernan Mar 14 '17 at 15:09
  • @DavidHeffernan you are confused here, please check your facts straight before making assumptions. `DUnitTestRunner.RunRegisteredTests;` is calling `Application.Initialize;` internally. – Wodzu Mar 15 '17 at 13:16
  • That's the detail that's missing from the question. If you think that the code in the question shows an author with a clear understanding of COM initialization then there's little we can do. Read my first comment. Right off the bat I went to the nun of the problem. I think I understand pretty well. – David Heffernan Mar 15 '17 at 13:25
  • You mean, detail that you've missed. I've never said that I have perfect understanding, quite the contrary, I've said that I don't understand different behaviour on different operating systems. I don't know how your first comment is a solution to the problem. I've called CoUnitialize because something else had initialized COM as STA and that was not the case of Application.Initialize. Lets leave that Application.Initialize because it has nothing to do with it. – Wodzu Mar 15 '17 at 13:58

1 Answers1

-1

Solution to my problem is described in this post. My mistake was calling CoUninitialize/CoInitialize after some other code already called it. CoInitialize should be called before any other code.

Wodzu
  • 6,932
  • 10
  • 65
  • 105
  • @DavidHeffernan I do have VCL app, you are wrong in this case mate. BTW: I "love" this community for giving me downvotes for a valid question and valid answer. Why do I even bother comming here :) – Wodzu Mar 15 '17 at 13:19
  • Not when you don't call Application.Initialize to initialise COM. Do you want to learn or not? It seems that you don't want to understand this at all. Why not? – David Heffernan Mar 15 '17 at 13:22
  • As I've said Application.Initialize is called but actually that is not the problem here because something else is initializing COM earlier. If you want I can learn you what is it. The question is, do you want to learn or not? – Wodzu Mar 15 '17 at 13:54
  • Which is what I said in my first comment. – David Heffernan Mar 15 '17 at 13:56
  • This was your first comment: _Why do you call CoUninitialize at all? What do you imagine registered already?_ – Wodzu Mar 15 '17 at 14:00
  • Indeed. That points at the problem. What is there that is initializing COM before the very first line of your .dpr file? Therein lies the solution to your problem. – David Heffernan Mar 15 '17 at 14:00
  • I've already answered that in my answer to the question. And you've commented wrongly that this is not the case becasue I don't call Application.Initialize which has nothing to do with it at the first place. – Wodzu Mar 15 '17 at 14:07