none
Problem creating an instance of a COM object RRS feed

  • Question

  • I think this is a general COM interface problem. I'm attempting to wrap the COM SAPI interface in a DLL that can allow a C application to convert text to speech. I can make a simple console C++ app that works fine.

    Sample code follows:

    int main( int argc, char** argv )
    {
     HRESULT hr;
     CComPtr<ISpVoice>   cpVoice;
     WCHAR szWTextString[156];

     hr = CoInitializeEx( NULL, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY );
     if( SUCCEEDED( hr ) )
     {
      hr = cpVoice.CoCreateInstance( CLSID_SpVoice );
      if( SUCCEEDED( hr ) )
      {
       lstrcpyW( szWTextString, L"This is a test" );
       hr = cpVoice->Speak( szWTextString, SPF_ASYNC, 0 );
       if( SUCCEEDED( hr ) )
        cpVoice->WaitUntilDone( INFINITE );
      }
     }
     return( 0 );
    }

     

    Then in my DLL, when I try to initialize the COM interface and create an instance of the ISpVoice object, the CoCreateInstance() call failse with an error that the class is not registered.

     

    I am missing something somewhere but I'm having a hard time findin the problem. Any help would be much appreciated.

    Friday, June 8, 2007 2:01 PM

Answers

  • It is good you posted.

    Gist is, move it. Move it to a place other than DllMain. DllMain is simply not the place to initialize anything other than setting up something simple.

     

    Please read this blog:

    http://blogs.msdn.com/larryosterman/archive/2004/04/23/118979.aspx

     

    You need to add an export to your dll and call that from the application to initialize.

    Friday, June 8, 2007 9:04 PM
  • DllMain is really not an exported function. It is not meant for service related initialization ( note , term service here is to mean services provided by the dll ). These are best left to expliclty exported functions by the dll.

     

    What DllMain is not important here ( since even if we could tell what it does today, we cannot say the same about a later OS ). Hence, best to trust the MS documentation and keep away from doing things like loading other dlls when our dll itself has not completed loading. In fact, probably the only module we can rely is loaded is kernel32.dll. Anything more and we are looking at undefined behavior and these are the most dangerous of all

    Monday, June 11, 2007 6:10 PM

All replies

  • Is the code in the dll same ? Could you post the exact code in the dll ?

     

    Friday, June 8, 2007 4:50 PM
  • Here is a snippet of the code:

     

    static CSt_mstts* pmstts = 0x00;

    BOOL APIENTRY DllMain( HANDLE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
          )
    {
        switch (ul_reason_for_call)
     {
      case DLL_PROCESS_ATTACH:
       if( ! pmstts )
       {
        pmstts = new CSt_mstts();
        if( pmstts )
        {
         if( ! pmstts->Init() )
         {
          pmstts->Release();
          delete ( pmstts );
          pmstts = 0x00;
         }
        }
       }
       break;

      case DLL_THREAD_ATTACH:
      case DLL_THREAD_DETACH:
       break;

      case DLL_PROCESS_DETACH:
       if( pmstts )
       {
        pmstts->Release( );
        delete( pmstts );
        pmstts = 0x00;
       }
       break;
        }
        return TRUE;
    }

     

    BOOL CSt_mstts::Init()
    {
     BOOL bRval = FALSE;
     HRESULT hr;
     CHAR szBuf[1024];
     PWCHAR pwChar = 0x00;

     if( ! m_bIntitialized )
     {
      hr = ::CoInitializeEx( NULL, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY );
      if( FAILED( hr ) )
      {
       lstrcpy( szBuf, "COM error 0x" );
       ltoa( hr, szBuf + lstrlen( szBuf ), 16 );
       lstrcat( szBuf, " " );

       FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, 0x00, hr, 0x00, szBuf + lstrlen( szBuf ), sizeof( szBuf ) - lstrlen( szBuf ), 0x00 );
       ST_WriteLog( "st_mstts", "Init", szBuf, ST_LOG_ERROR );
      }

      hr = m_cpVoice.CoCreateInstance( CLSID_SpVoice );
      if( FAILED( hr ) )
      {
       lstrcpy( szBuf, "COM error 0x" );
       ltoa( hr, szBuf + lstrlen( szBuf ), 16 );
       lstrcat( szBuf, " " );

       FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, 0x00, hr, 0x00, szBuf + lstrlen( szBuf ), sizeof( szBuf ) - lstrlen( szBuf ), 0x00 );
       ST_WriteLog( "st_mstts", "Init", szBuf, ST_LOG_ERROR );
      }

    .

    .

    .

    }

    Friday, June 8, 2007 6:32 PM
  • It is good you posted.

    Gist is, move it. Move it to a place other than DllMain. DllMain is simply not the place to initialize anything other than setting up something simple.

     

    Please read this blog:

    http://blogs.msdn.com/larryosterman/archive/2004/04/23/118979.aspx

     

    You need to add an export to your dll and call that from the application to initialize.

    Friday, June 8, 2007 9:04 PM
  • Thanks.

     

    That is the first constructive bit of advice I've gotten and it worked. I moved the initialization code to somewere else and that did the trick. Just for further knowledge, why is it that putting the code in the DllMain is not OK. Is it just too early in the initialization process?

    Monday, June 11, 2007 4:20 PM
  • DllMain is really not an exported function. It is not meant for service related initialization ( note , term service here is to mean services provided by the dll ). These are best left to expliclty exported functions by the dll.

     

    What DllMain is not important here ( since even if we could tell what it does today, we cannot say the same about a later OS ). Hence, best to trust the MS documentation and keep away from doing things like loading other dlls when our dll itself has not completed loading. In fact, probably the only module we can rely is loaded is kernel32.dll. Anything more and we are looking at undefined behavior and these are the most dangerous of all

    Monday, June 11, 2007 6:10 PM