Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

faulty Asio driver present (without device) causes exception crash on PaAsio_Initialize #960

Open
cuikp opened this issue Sep 13, 2024 · 1 comment
Labels
src-asio Steinberg ASIO Host API /src/hostapi/asio

Comments

@cuikp
Copy link

cuikp commented Sep 13, 2024

Recently began using portaudio via P/Invoke. Apparently portaudio attempts to load (and then unload) all ASIO drivers present on a given machine in PaAsio_Initialize(). However, if a driver is faulty and fails to load, portaudio will still try to unload it, causing an exception to be thrown.

This particular situation may be difficult to reproduce unless one purposely installs a faulty driver and then runs portaudio. As luck had it I happened to have a bad ASIO driver already on my system so I discovered the problem straightaway.

Tinkering with the source code, I find that the exception is thrown in the ASIO SDK in:

LONG AsioDriverList::asioCloseDriver (int drvID)
{
	LPASIODRVSTRUCT	lpdrv = 0;
	IASIO			*iasio;

	if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
		if (lpdrv->asiodrv) {
			iasio = (IASIO *)lpdrv->asiodrv;
			iasio->Release();
			lpdrv->asiodrv = 0;
		}
	}

	return 0;
}

However, this code will not try to unload the (faulty) driver if lpdrv->asiodrv is nullptr, so this opens up a workaround (the following), which I have tested and thus managed to avoid the driver error.

In the static PaError InitPaDeviceInfoFromAsioDriver() method (pa_asio.cpp) I added the following in the else block:

 if( result == paNoError )
 {
      ...
  }
  else
  {
      //PA_DEBUG(("Loading result was NOT successful for:%d name = %s\n", driverIndex, deviceInfo->name));
      **return PaErrorCode::paNotInitialized;**
  }

That is, we can return the specific PaErrorCode of paNotInitialized when attempt to get the device info fails.

Then, I made the following change when the call is actually made from PaAsio_Initialize():

PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
...


 for( i=0; i < driverCount; ++i )
 {
  PA_DEBUG(("ASIO names[%d]:%s\n",i,names[i]));

 ...

/* Attempt to init device info from the asio driver... */
{
    PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
    PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo;

    deviceInfo->structVersion = 2;
    deviceInfo->hostApi = hostApiIndex;

    deviceInfo->name = names[i];

    PaError InitInfo = InitPaDeviceInfoFromAsioDriver(asioHostApi, names[i], i, deviceInfo, asioDeviceInfo);
    //if( InitPaDeviceInfoFromAsioDriver( asioHostApi, names[i], i, deviceInfo, asioDeviceInfo ) == paNoError )
    if( InitInfo == paNoError )
    {
        (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
        ++(*hostApi)->info.deviceCount;
    }
    else
    {
       //////////// ADDED ELSE BLOCK HERE /////////////////////////////
         if (InitInfo == PaErrorCode::paNotInitialized) {
            
            LPASIODRVSTRUCT thisLP = asioHostApi->asioDrivers->lpdrvlist;

            for (int j = 0; j < i && thisLP != nullptr; ++j) {
                thisLP = thisLP->next;
            }

            if (thisLP != nullptr)
                thisLP->asiodrv = nullptr;
        }

        OutputDebugStringA(("Skipping ASIO device: " + std::to_string(i) + " " + names[i] + "\n\n").c_str());
       continue;
       ///////////////////////////////////////////////////
    }

In other words, if the returned PaErrorCode is paNotInitialized, it loops through the LPASIODRVSTRUCT to find the current one, and sets its asiodrv to nullptr. Once set to nullptr, the SDK's methods AsioDriverList::asioCloseDriver() (as well as deleteDrvStruct() ) will skip trying to unload it, thus solving the problem.

  • OS: [Win 11]
  • PortAudio version: latest on github (v.19.7.0)
  • ASIO

(A pull request could be made if this explanation is not clear enough to implement.)

@cuikp cuikp changed the title Asio driver present without device causes crash on PaAsio_Initialize faulty Asio driver present (without device) causes exception crash on PaAsio_Initialize Sep 13, 2024
@RossBencina
Copy link
Collaborator

I think it's worth submitting working code as a draft PR so that we know exactly what you're proposing.

Is there any way I could install this crashing driver?

Also, you wrote:

I find that the exception is thrown in the ASIO SDK

Where is the exception caught?

@RossBencina RossBencina added the src-asio Steinberg ASIO Host API /src/hostapi/asio label Sep 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
src-asio Steinberg ASIO Host API /src/hostapi/asio
Projects
None yet
Development

No branches or pull requests

2 participants