Announcement

Collapse
No announcement yet.

Probleme mit COM-Server

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Probleme mit COM-Server

    Hi,

    ich hab ein COM-Server, der einen Eintrag in die Running Object Table macht...

    Beim schliessen des Servers kommt immer eine Warnung:
    "Diese Anwendung enthält noch aktive Com-Objekte. Ein oder mehrere Client-Anwendungen können Verweise auf diese Objekte haben. Manuelles schliessen dieser Anwendung könnte dazu führen, dass client-Anwendungen fehlerhaft ausgeführt werden. Sind sie sicher, dass diese Anwendung geschlossen werden soll ? [Ja] [Nein]"
    Wie krieg ich die weg ? Es scheint ja so, als hängen noch Objekte am Server oder so... der Quellcode is so:

    var
    lpROT : IRunningObjectTable;<br>
    monikerFilePath : PWideChar;<br>
    monikerInterface : IMoniker;<br>
    rootStorage: IStorage;<br>
    rotKey : integer;<br>
    clrKey : integer;<br>
    hr: HRESULT;<br>
    il: INCC;

    begin<br>
    CoInitializeEx(nil,COINIT_APARTMENTTHREADED);<br>
    monikerFilePath := 'c:\temp\NCCMoniker.id';<br>

    hr := CoRegisterClassObject (CLASS_NCC,NCCFactory,CLSCTX_LOCAL_SERVER,REGCLS_M ULTI_SEPARATE,clrKey);<p>

    if not succeeded(StgOpenStorage(monikerFilePath, nil, STGM_READWRITE or STGM_SHARE_EXCLUSIVE, nil, 0, rootStorage)) then begin<br>
    StgCreateDocFile(monikerFilePath, STGM_CREATE or STGM_READWRITE or STGM_SHARE_EXCLUSIVE, 0, rootStorage);<br>
    WriteClassStg(rootStorage,CLASS_NCC);<br>
    end;<p>

    rootStorage := nil;<p>

    hr := CreateFileMoniker(monikerFilePath,monikerInterface );<br>
    hr := GetRunningObjectTable(0,lpROT);<br>
    il := CoNCC.Create;<br>

    hr := lpROT.Register(ROTFLAGS_ALLOWANYCLIENT or ROTFLAGS_REGISTRATIONKEEPSALIVE, il as IUnknown, monikerInterface, rotKey);<p>

    Application.Initialize;<br>
    Application.CreateForm(TForm1, Form1);<br>
    Application.Run;<br>

    hr := lpROT.Revoke(rotKey);<br>
    il := nil;<br>
    //hr := CoRevokeClassObject(clrKey); // keine ahnung ob das rein muss?..
    //ändert jedenfalls nix an der
    //Meldung..<br>
    end;<p>

    Achso...die Warnung kommt noch vor dem Revoke...

    Was mache ich denn falsch ? Bitte kann mir jemand helfen ?

    Danke schonmal<br>
    dinchen

  • #2
    Hallo,

    da Borland die COM-Prinzipien fest in die Sprache <i>Object Pascal</i> (Neu: <i>Delphi</i>) und in den Compiler eingebaut hat, macht es mit Delphi nicht viel Sinn, ein vorgefundenes C/C++-Beispiel 1:1 nachzubauen.

    Über die API-Funktion <b>RegisterActiveObject</B> wird eine bereits ausgeführt Instanz eines COM-Objekts als aktives Objekt der Klasse (CLSID) systemweit registriert. COM nimmt daraufhin dieses Objekt in seine globale Verzeichnistabelle auf. Zu einem späteren Zeitpunkt kann ein Client das so registrierte Objekt jederzeit anfordern, ohne daß eine neue Instanz des Objekt erstellt werden muß. Selbstverständlich muß der COM-Server auch wieder dafür sorgen, daß dieses Objekt sofort wieder abgemeldet wird, wenn die Instanz des COM-Servers zerstört wird. Die TAutoObject-Klasse stellt dazu bereits zwei passende Methoden bereit:
    <pre>
    procedure TROTServer2.Initialize;
    begin
    inherited Initialize;
    RegisterActiveObject(Self as IUnknown, CLASS_ROTServer2,
    ACTIVEOBJECT_WEAK, FSrvROTHandle);
    end;

    destructor TROTServer2.Destroy;
    begin
    RevokeActiveObject(FSrvROTHandle, nil);
    inherited Destroy;
    end;
    </pre>
    Noch ein Wort zum dritten Parameter - über den Wert <b>ACTIVEOBJECT_WEAK</B> teilt das COM-Objekt mit, daß in der ROT nur ein Zeiger auf das Objekt gespeichert werden soll, ohne den Verwendungszähler des Objekts zu blockieren. Damit entfernt COM alle Verweise, sobald der letzte Client seine Referenz auf das COM-Objekt abgebaut hat. Alle Automation-Server sollen den Wert ACTIVEOBJECT_WEAK verwenden, so daß auch dieses Beispielprogramm keine Ausnahme macht.

    Wird jedoch ACTIVEOBJECT_STRONG genutzt, sperrt sich der COM-Server in der ROT selbst, so dass die Instanz unabhängig von der Referenzzählung der Interface-Zeiger immer am Leben bleibt. In der Platform SDK-Dokumenation ist auf der Hilfeseite zu IRunningObjectTable der folgende Satz zu finden: "<i>ROTFLAGS_REGISTRATIONKEEPSALIVE. When set, indicates a strong registration for the object.</i>".

    Comment


    • #3
      HI,

      hm, versteh ich nich so ganz...<br>
      Wo soll denn das in den Quelltext rein?<br>

      meinst du statt <br>
      CoRegisterClassObject(CLASS_NCC,NCCFactory,CLSCTX_ LOCAL_SERVER,REGCLS_MULTI_SEPARATE,clrKey);<br>
      sollte ich <br>
      RegisterActiveObject(NCCFactory as IUnknown, CLASS_NCC,
      ACTIVEOBJECT_WEAK, clrKey);
      aufrufen ?

      Wär echt toll, wenn du mein Quellcode in die richtige Weise umwandeln würdest (denke das dürfte nich allzuviel sein?? *hoff*)...

      Mfg
      dinche

      Comment


      • #4
        Hallo,

        normalerweise greift man in Delphi auf die Experten (hier: <i>Automatisierungsobjekt-Experte</i>) zurück, damit das Grundgerüst für das COM-Objekt und die Class Factory zusammengebaut wird. Die Nutzfunktion wird dann von Hand ergänzt. Das vollständige Beispiel eines COM-Objekts, das sich in der ROT registriert, sieht daher wie folgt aus:
        <pre>
        unit ROTSrv2Impl;

        interface

        uses
        ComObj, ActiveX, AxCtrls, ROTSrv2_TLB;

        type
        TROTServer2 = class(TAutoObject, IROTServer2)
        private
        { Private-Deklarationen}
        FClientCount : Integer;
        FClientMessage : WideString;
        FSrvROTHandle : Integer;
        public
        procedure Initialize; override;
        destructor Destroy; override;
        protected
        { Protected-Deklarationen }
        procedure Connect(CltID: Integer; ConnectTime: TDateTime;
        out SrvMsg: WideString); safecall;
        function Get_GetCount: Integer; safecall;
        function Get_ClientMessage: WideString; safecall;
        procedure Set_ClientMessage(const Value: WideString); safecall;
        end;

        implementation

        uses ComServ, ROTSrv2Frm, SysUtils;

        var
        iGlobalCount : Integer;

        { COM-Objekt in der ROT (Running Object Table) anmelden }

        procedure TROTServer2.Initialize;
        begin
        inherited Initialize;
        RegisterActiveObject(Self as IUnknown, CLASS_ROTServer2,
        ACTIVEOBJECT_WEAK, FSrvROTHandle);
        end;

        { COM-Objekt muß am Ende wieder aus der ROT (Running Object Table)
        entfernt werden. }

        destructor TROTServer2.Destroy;
        begin
        RevokeActiveObject(FSrvROTHandle, nil);
        inherited Destroy;
        end;

        procedure TROTServer2.Connect(CltID: Integer; ConnectTime: TDateTime;
        out SrvMsg: WideString);
        begin
        Inc(FClientCount);
        Inc(iGlobalCount);
        SrvMsg := Format('Clientcount: %d; GlobalCount: %d',
        [FClientCount, iGlobalCount]);
        FormServer.ListBox1.Items.Add(Format('Connect %d um %s (%s)', [CltID,
        TimeToStr(ConnectTime), SrvMsg]));
        end;

        function TROTServer2.Get_GetCount: Integer;
        begin
        Result := FClientCount;
        end;

        function TROTServer2.Get_ClientMessage: WideString;
        begin
        Result := FClientMessage;
        end;

        procedure TROTServer2.Set_ClientMessage(const Value: WideString);
        begin
        FClientMessage := Value;
        end;

        initialization
        TAutoObjectFactory.Create(ComServer, TROTServer2, Class_ROTServer2,
        ciMultiInstance, tmApartment);

        end.
        </pre>
        Das direkte Herumhacken in der .DPR-Datei des Projekts ist unüblich - und sogar gefährlich, da die COM-Zugriffe noch <b>vor</b> <i>Application.Initialize</i> stattfinden. Aus diesem Grund taucht ja auch die Zeile <i>CoInitializeEx(nil,COINIT_APARTMENTTHREADED); </i> gleich am Anfang auf - denn diese Operation würde erst von <i>Application.Initialize</i> automatisch erledigt.

        Der Delphi-Experte hat automatisch zwei <b>Class Functions</b> erzeugt, damit die Instanz des COM-Objekts bequem über den Aufruf a la <i>FROTSrv := CoROTServer2.Create;</i> erzeugt werden kann. In diesem Fall kümmert sich Delphi (... TAutoObject...) um alles weitere

        Comment


        • #5
          Siehe auch das folgende Beispiel

          Comment

          Working...
          X