Avatar billede codecow Nybegynder
29. juli 2005 - 16:25 Der er 1 løsning

Modtage events fra en .NET COM i et konsol projekt.

Hej.

Jeg har en C++ konsol klient som benytter en .NET DLL som et COM modul.
DLL'en sender en event hvert sekund.

Problemet er at hvis jeg slukker for tråden som indeholder EventSink objektet, og derefter lukker ned for programmet
får jeg, efter at main har returnet 0, en access violation i mscorwks.dll

Når jeg undersøger addressen som fejlen opstår på, kan jeg se at det er i tråden hvor EventSink objektet er instancieret hvor fejlen sker.

Da tråden er termineret er der ikke noget at sige til at hukommelses omraådet ikke kan læses, men hvad skal mscorwks.dll læse der?

Jeg antager at det er garbage collectionen som ikke har fået mulighed for at fjerne referencen til eventsinken.... men jeg er ikke sikker,
og hvis jeg lader programmet sleepe et øjeblik inden return 0 hjælper det heller ikke.

Hvis jeg tilgengæld lader tråden kører og lader main returnere så er der fryd og gammen over hele linien.

Hvordan skal man consume events i et konsol projekt ? Eller rettere sagt: hvordan stopper man med at consume events i et konsol projekt?

Alt hjælp modtages med tak :)

Henrik.


//--------------------------------------------------------------------------------------------------------------
// Klient kode:

#include "stdafx.h"
#include <iostream>
#include <tchar.h>
#include <atlbase.h>
CComModule _Module;
#include <atlcom.h>
#import "mscorlib.tlb" named_guids rename("ReportEvent", "_ReportEvent") \
    rename( "_Module", "_NETModule" )

#import "C:\Developer\c++\Sniplets\CsComEventServer\CsComEventServer\bin\Debug\CsComEventServer.tlb" \
    no_namespace named_guids

using namespace std;

// prototypec
char* ConvertLPWSTRToLPSTR (LPWSTR lpwszStrIn);
char* ConvertBSTRToLPSTR (BSTR bstrIn);

// globals
_Server* pServer = NULL;
HANDLE hExitEvent = NULL;
HANDLE hThreadReadyEvent = NULL;
CRITICAL_SECTION criticalSection;

// ATL stuff
_ATL_FUNC_INFO OnUserEventInfo =
    { CC_STDCALL, VT_EMPTY, 1 /*num args*/, {VT_BSTR}};

//-----------------------------------------------------------------------------
class EventSink :
    public IDispEventSimpleImpl
        < 1 /*id*/, EventSink, &DIID__DTestEventInterface >
{
private:
    _Server* pDotNetObject;
public:
    BEGIN_SINK_MAP( EventSink ) 
        SINK_ENTRY_INFO(
            1 /*id*/,
            DIID__DTestEventInterface,
            1 /*disp id*/,
            OnUserEvent,
            &OnUserEventInfo )
    END_SINK_MAP()


    void __stdcall OnUserEvent( BSTR msg ) {
        USES_CONVERSION;
        char buf[256];
        SecureZeroMemory( buf, sizeof( buf ));
        sprintf( buf, "Message is: %s", ConvertBSTRToLPSTR( msg ));
        cout << buf << endl;
        SysFreeString( msg );
    }

    EventSink( _Server* pObj ) {
        pDotNetObject = pObj;
        pDotNetObject -> AddRef();
        HRESULT hres = DispEventAdvise((IUnknown*) pDotNetObject );
        if( hres != S_OK ) {
            char buf[256];
            CComBSTR bError;
            IErrorInfoPtr ipError;
            ::GetErrorInfo( 0, &ipError );
            ipError->GetDescription( &bError );
            SecureZeroMemory( buf, sizeof(buf));
            sprintf( buf, "EventSink constructor failed.\nhres: 0x%X.\nDescription: %s",
                hres, ConvertLPWSTRToLPSTR(bError));
            ::MessageBox( 0, buf, "ERROR", 0 );
        }
    }

    ~EventSink() {
        pDotNetObject -> Release();
        HRESULT hres = DispEventUnadvise((IUnknown*) pDotNetObject );
        if( hres != S_OK ) {
            char buf[256];
            CComBSTR bError;
            IErrorInfoPtr ipError;
            ::GetErrorInfo( 0, &ipError );
            ipError->GetDescription( &bError );
            SecureZeroMemory( buf, sizeof(buf));
            sprintf( buf, "EventSink destructor failed.\nhres: 0x%X.\nDescription: %s",
                hres, ConvertLPWSTRToLPSTR(bError));
            ::MessageBox( 0, buf, "ERROR", 0 );
        }
    }
};

//-----------------------------------------------------------------------------
DWORD WINAPI ThreadFunc( PVOID pvParam ) {
    DWORD dwResult = 0;
    EventSink theSink( pServer );
    SetEvent( hThreadReadyEvent );

    WaitForSingleObject( hExitEvent, INFINITE );

    cout << "Got exit event" << endl;

    return(dwResult);
}

//-----------------------------------------------------------------------------
int main() {

    USES_CONVERSION;
    HRESULT hres;
    hres = CoInitialize( NULL );
    if( hres != S_OK ) {
        return -1;
    }
   
    try {
        hres = CoCreateInstance(
            CLSID_Server, NULL, CLSCTX_SERVER,
            IID__Server, (void**) &pServer );
        if( hres != S_OK ) {
            ::MessageBox( 0, "Could not instanciate COM server", "Error", 0 );
            return -1;
        }

        hExitEvent = CreateEvent( NULL, false, false, NULL );
        hThreadReadyEvent = CreateEvent( NULL, false, false, NULL );
        HANDLE hThread = CreateThread( NULL, 0, ThreadFunc, NULL, 0, NULL );
        WaitForSingleObject( hThreadReadyEvent, 100 );
        CloseHandle( hThreadReadyEvent );

        pServer->StartEventSource();

        cout << "Press any key to exit" << endl;
        char a;
        cin >> a;

        pServer->StopEventSource();

        SetEvent( hExitEvent );
        CloseHandle( hExitEvent );

        CoUninitialize();
        pServer = NULL;

    } catch(_com_error &e) { 
        cout << "Error from source: " << e.Source() << endl;
        cout << "Description: " << e.Description() << endl;
        cout << "ErrorMessage: " << e.ErrorMessage() << endl;
    }

    return 0;
}


//-----------------------------------------------------------------------------
char* ConvertLPWSTRToLPSTR( LPWSTR lpwszStrIn ) {
    LPSTR pszOut = NULL;

    if (lpwszStrIn != NULL) {
        #pragma warning( push )
        #pragma warning( disable : 4267 )
        int nInputStrLen = wcslen (lpwszStrIn);
        #pragma warning( pop )

        // Double NULL Termination
        int nOutputStrLen = WideCharToMultiByte( CP_ACP, 0, lpwszStrIn, nInputStrLen, NULL, 0, 0, 0 ) + 2;   
        pszOut = new char[ nOutputStrLen ];
        if( pszOut ) {
            memset( pszOut, 0x00, nOutputStrLen );
            WideCharToMultiByte( CP_ACP, 0, lpwszStrIn, nInputStrLen, pszOut, nOutputStrLen, 0, 0 );
        }
    }
    return pszOut;
}

//-----------------------------------------------------------------------------
char* ConvertBSTRToLPSTR( BSTR bstrIn ) {
    LPSTR pszOut = NULL;

    if( bstrIn != NULL ) {
        int nInputStrLen = SysStringLen( bstrIn );

        // Double NULL Termination
        int nOutputStrLen = WideCharToMultiByte( CP_ACP, 0, bstrIn, nInputStrLen, NULL, 0, 0, 0 ) + 2;   
        pszOut = new char[ nOutputStrLen ];
        if( pszOut ) {
            memset( pszOut, 0x00, sizeof (char)*nOutputStrLen );
            WideCharToMultiByte( CP_ACP, 0, bstrIn, nInputStrLen, pszOut, nOutputStrLen, 0, 0 );
        }
    }
    return pszOut;
}



//--------------------------------------------------------------------------------------------------------------------------------

// HER er .NET DLL'en:




using System;
using System.Threading;
using System.Runtime.InteropServices;

namespace CsComEventServer {
   
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [Guid("AF7A1FCE-4A05-4716-9F5C-9CBCC3450239")]
    public interface _DTestEventInterface {
        [DispId(1)] void SomeEvent( string field );
    }

    [ComSourceInterfaces(typeof(_DTestEventInterface))]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ProgId("blah.TestEventServer")]
    [Guid("2E4EDF44-85B8-4476-93C0-4716974ACB18")]
    public class Server {

        [ComVisible(false)]
        public delegate void SomeEventHandler( string field );
        public event SomeEventHandler SomeEvent;
        private bool _terminateFlag = false;
        private Thread EventThread;

        public Server() {}

        public void TriggerEvent() {
            SomeEvent( "hej" );
        }

        private void EventSourceThread () {
            while( ! _terminateFlag ) {
                this.TriggerEvent();
                Thread.Sleep( 1000 );
            }
        }

        public void StartEventSource() {
            EventThread = new Thread( new ThreadStart( this.EventSourceThread ));
            EventThread.Start();
        }

        public void StopEventSource() {
            EventThread.Suspend();
        }
    }
}
Avatar billede codecow Nybegynder
01. august 2005 - 16:13 #1
Fandt svaret selv :)

Hvis andre skulle være interesseret, så er det en bug i .NET 1.1

Opdater til Beta 2.0 og det virker.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester