none
Compilando en modo Debug no hay problema, compilando en modo Release genera una infracción de acceso. RRS feed

  • Pregunta

  • Hola a todos, gracias por su tiempo; últimamente estuve desarrollando una aplicación con la API de telefonía de Windows (TAPI 2), para recibir y enviar sonido por la línea telefónica a través de un módem. Si bien no tuve inconvenientes con TAPI, se me presenta el siguiente problema:

    Al recibir la llamada, mi aplicación, mediante TAPI, atiende la llamada e inicia el proceso de grabación de audio. Para la grabación de audio utilizo <mmsystem> y <fstream>. Inicializo correctamente el dispositivo de sonido, logro obtener unos segundos de sonido, pero antes de terminar la llamada, la Windows me informa un error en la aplicación. Más adelante me di cuenta que el problema era una infracción de acceso.

    Ahora, lo raro del asunto, es que utilizando la aplicación en modo Debug, puedo grabar de la línea telefónica sin ningún problema, pero cuando ejecuto la aplicación en modo Release, graba los primeros 2 o 3 segundos, y luego se genera la infracción de acceso. Estuve leyendo en foros y buscando en Internet sobre este problema y me enteré que se puede depurar esta compilación en modo Release, pero aún compilándolo con opciones de depuración, el depurador me dice que "No se cargaron símbolos" y lo único que me da es la línea del programa en ensamblador donde se produce el problema.

    Probando la grabación de audio utilizando WAVE_MAPPER como dispositivo de grabación, puedo grabar audio desde un micrófono, o inclusive desde StereoMix, sin ningún problema, tanto en modo Debug como en modo Release.

    Espero puedan ayudarme, si es necesario el código avisenme, no lo puse ahora porque es algo extenso. Desde ya, muchas gracias.

    jueves, 17 de enero de 2013 22:47

Respuestas

  • Ok, perdón por la tardanza.

    No tengo experiencia con las funciones de audio en Windows así que me cuesta un poco.  Sí veo algo que es potencialmente su problema:  La líena waveInPrepareHeader(hMic, &whIn, sizeof(WAVEHDR)); en el case WIM_DATA (y la siguiente línea).  La variable whIn es una variable en el stack y usted está usándola para una función asíncrona.  Muy malo.  En el momento en que la función waveInCallback() acabe dicha variable es destruida.  Usted DEBE crear el WAVEHDR en el heap dinámicamente para asegurarse que el header es válido todo el tiempo (y por supuesto destruirlo cuando ya no es necesario).

    Si arregla eso, arreglará su problema.  99.99% seguro.


    Jose R. MCP
    Code Samples

    • Propuesto como respuesta webJoseModerator domingo, 3 de febrero de 2013 2:45
    • Marcado como respuesta Mr. Hell domingo, 3 de febrero de 2013 19:14
    domingo, 3 de febrero de 2013 2:45
    Moderador

Todas las respuestas

  • Pues lo que ocupa es generar el PDB para Release.  Esos serán los símbolos de su ejecutable.  Adicionalmente ocupará los símbolos de Microsoft, pero Visual Studio puede bajarlos de Internet automáticamente.  ¿Está usted generando PDB's para todos sus componentes (ejecutables y bibliotecas creadas por usted)?  ¿Está usted usando bibliotecas de terceras personas fuera de Microsoft Corporation?

    Jose R. MCP
    Code Samples

    viernes, 18 de enero de 2013 23:52
    Moderador
  • El PDB para Release lo tengo, de hecho, probé de generar un error deliberadamente (infracción de acceso):

    void GenerarError(void)
    {
        char* vMem = (char*)0xB8000;
        vMem[0] = 'E'; //Acá se genera el error
    }

    Y el depurador me mostró la línea de código sin problemas, pero cuando se genera el otro error, por el cual estoy aquí, me dice que no se han cargado símbolos y me muestra el desensamblador.

    sábado, 19 de enero de 2013 16:27
  • Creo que VS dice el nombre del binario del cual no pudo cargar símbolos.  ¿Cuál es el binario?  ¿Ese binario es suyo o es de alguien más (Microsoft u otro)?  Porque no tiene mucho sentido que pueda cargar los símbolos para un error y para otro no.  ¿Tiene su VS configurado para descargar los símbolos desde los servidores de Microsoft?

    Jose R. MCP
    Code Samples

    sábado, 19 de enero de 2013 16:53
    Moderador
  • Eso es porque el problema se produce dentro de alguna parte del código que no tiene los símbolos disponibles.

    Si Has configurado Visual Studio para que se los baje de Microsoft, y te sigue saliendo, o bien el problema está en alguna librería de terceros que tengas por ahí o en código de Microsoft del cual no suministren símbolos (que lo hay).

    En general ese tipo de problemas viene causado por "encadenamientos" de errores. Tu código tiene un error que no afecta al funcionamiento, y luego tu código llama a rutinas del sistema y el error se produce por ahí debajo y entonces salta. Una forma de intentar ver qué está pasando es mirar la pila de llamadas y ver tu código en el debugger.

    Prueba a entrar en Debug|Exceptions y activa "first chance" para la excepción que se dispara, a ver si suena la rana.


    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/

    lunes, 21 de enero de 2013 8:38
    Moderador
  • Excepción no controlada en 0x68051E39 en audioStream.exe:

    0xC0000005: Infracción de acceso al escribir en la ubicación 0x79466C76.

    Ese es el mensaje que me da el depurador (por supuesto, audioStream es el nombre de mi ejecutable).

    Al elegir la opción "Interrumpir", me dice que no se cargaron símbolos para ningún marco de llamadas.

    Con respecto a la segunda respuesta, no estoy usando librerías de terceros, todos mis includes son a librerías de Microsoft y no tengo ninguna llamada a LoadLibrary ni a GetProcAddress. No se como configurar Visual Studio para que los descargue de Microsoft, todos los símbolos que tengo son los que vienen en la instalación.

    stdafx.h:

    #include <windows.h>
    #include <mmsystem.h>
    
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    #pragma comment(lib, "winmm")


    Ese código es el que está por debajo del encabezado precompilado que crea el asistente (stdafx.h)

    Con respecto a lo de Debug | Exceptions -> First Chance, no logré encontrar esa opción, o quizas la busqué mal, mi VS esta en español, por lo que fui al menú Depurar | Excepciones..., donde me aparecieron un conjunto de items, pero no encontré en ninguno esa opción.

    Gracias a todos por la ayuda.

    P.D: En la Pila de Llamadas me aparece arriba de todo (con la flecha amarilla) "serwvdrv.dll", abajo de eso que no existen o no se pudieron cargar símbolos para la DLL anteriormente mencionada y abajo de eso Kernel32.dll. (Creo que la excepción se produce en kernel32.dll).

    Descargué los símbolos desde el sitio de Microsoft, y ahora me aparece que la DLL en la que se produjo el error es "serwvdrv.dll" (no me muestra el código fuente), pero sigo sin darme una idea de como solucionarlo.

    Obteniendo un poco de información que causa el error, encontré que "serwvdrv.dll" es el controlador de audio Unimodem.

    • Editado Mr. Hell lunes, 21 de enero de 2013 16:30
    lunes, 21 de enero de 2013 16:10
  • Ok, entonces no tiene símbolos para serwvdrv.dll.  ¿Este DLL está firmado digitalmente por alguna compañía?  Es que dudo que sea de Microsoft pues MS es muy bueno en mantener símbolos para todos sus binarios.

    En fin, teniendo símbolos o no, la excepción en este DLL podría estarse dando por datos que vienen de otras funciones.  Ubique en el stack el frame más próximo a la excepción que sea código suyo y parta de ahí.  Revise los punteros, datos, variables, etc.  En alguna parte debe de haber algo incorrecto, claro está, asumiendo que serwdrv.dll está operando como se espera.


    Jose R. MCP
    Code Samples

    lunes, 21 de enero de 2013 17:06
    Moderador
  • El binario sí es de Microsoft, según sus propiedades, es parte de "Sistema Operativo Microsoft Windows (r)", y está en su versión 5.1.2600 (Windows XP). 

    Según VS, se descargaron los símbolos desde el sitio Web de Microsoft (obtuve la información sobre como hacerlo desde MSDN), pero no hay código fuente disponible para el mismo. De hecho, según el depurador, la excepción se produce en una función dentro del binario llamada "ReadCompletionHandler", pero no pude obtener más que esa información. Probé usar el mismo binario pero con la versión de Windows 2000 y tuve el mismo problema.

    lunes, 21 de enero de 2013 17:25
  • Ah sí, correcto.  El código fuente no está disponible, únicamente los símbolos para el stack.  Como le dije antes, busque en el stack el frame más próximo a la excepción y empiece de ahí a verificar su código.

    Jose R. MCP
    Code Samples

    lunes, 21 de enero de 2013 17:46
    Moderador
  • Aparentemente el problema se produce después de agregar el buffer nuevo a la cola. Al iniciar la grabación, mediante waveInAddBuffer se agregan 2 buffers, de 16000 bytes cada uno, de modo que cada uno es capaz de guardar un segundo de audio. Luego, el Callback va intercambiando los buffers, e informando al hilo que guarda el archivo en el disco duro que puede continuar. Según estoy viendo, al iniciar la grabación desde el dispositivo Unimodem, se producen automáticamente 4 mensajes WIM_DATA, cuya causa desconozco, donde se me informa que se grabaron una cantidad de bytes que no tienen nada que ver con el tamaño de los buffers (960, 2 millones, etc.), y el 4 mensaje WIM_DATA, me informa que se grabaron 16000 bytes, que es la cantidad de datos esperada. El problema es que no existe un 5to mensaje WIM_DATA, sino que ahí se produce la excepción, y no logro entender el por qué.

    Con respecto al trabajo con los buffers, creo que no hay ningún problema puntual ahí, dado que, como dije en el primer mensaje, utilizando mi placa de sonido no tengo problema alguno, puedo grabar sin ningún problema, y el archivo generado lo reproduce el Reproductor de Windows Media sin ningún problema también.

    Adjunto lo que mi consola muestra:

    Línea #1 del módem
    Formatos: 0
    Presione una tecla para continuar... //Espera a que el usuario inicie la grabación
    WIM_OPEN
    Dispositivo abierto
    WIM_DATA
    Bytes grabados: 15996
    WIM_DATA
    Bytes grabados: 15996
    WIM_DATA
    Bytes grabados: 960
    Presione una tecla para continuar... //Detiene la grabación (se supone que aparezca justo despues de WIM_OPEN)
    WIM_DATA
    Bytes grabados: 273397828
    WIM_DATA
    Bytes grabados: 15992

    Justo después del último WIM_DATA (bytes grabados = 15992), se produce la excepción. Los mensajes WIM_DATA que aparecen antes del segundo "Presione una tecla para continuar..." salen antes de que se cumpla 1 segundo, y deberían durar por lo menos 4 segundos, dado que cada buffer es de 1 segundo.

    Como se puede observar uno de los WIM_DATA informa 273.397.828 bytes grabados, lo cual es imposible, dado que sobrecargaría el buffer, sin embargo, ahí no se produce la excepción.

    Probando la misma aplicación, pero con la placa de sonido, todo se produce como se espera, no hay ningún WIM_DATA antes del segundo "Presione una tecla para continuar..." y no hay excepciones.

    • Editado Mr. Hell lunes, 21 de enero de 2013 19:12
    lunes, 21 de enero de 2013 18:57
  • Vale, vamos aclarando cosas.

    Un error 0xC0000005 es una escritura no válida, y por lo que cuentas me juego un gallifante a que no estás asignando los nuevos bloques de memoria, o lo estás haciendo pero no los estás pasando bien.

    El que un programa funcione bien en debug y mal en release con este tipo de problemas se debe a que en debug la memoria está más "repartida" y hay más "huecos" (que realmente son "trampas" para que el debugger pille al programado in-fraganti) y en release no.

    Una prueba que podrías hacer es escribir tu mismo sobre el buffer de memoria antes de pasarlo. Si te falla entonces es que lo estás asignando mal, y si no, es que lo estás pasando.


    MVP Visual C++ - Visita mi blog sobre desarrollo: http://geeks.ms/blogs/rfog/

    martes, 22 de enero de 2013 10:50
    Moderador
  • Realmente no creo que los bloques estén mal asignados por dos razones, la primera y principal es que utilizando la placa de sonido (el único parámetro modificado del programa es waveInOpen(..., WAVE_MAPPER, ...)), la aplicación funciona sin problemas, y se están usando el mismo Callback, los mismos buffers y exactamente la misma configuración; y la segunda razón, es que los buffers se asignan mediante [malloc()] al inicio del programa y únicamente se des-asignan [free()] al final del programa, cuando waveInStop detuvo el dispositivo y waveInClose lo cerró correctamente. Con respecto a la re-asignación de buffers mediante waveInAddBuffer y waveInPrepareHeader, se utilizan los mismos parámetros que al inicio de programa, por lo tanto, no debería fallar tampoco ahí.

    Resultados de la escritura en el buffer antes de pasarlo: 

    Callback de waveIn con código para pruebas: 

    void CALLBACK waveInCallback(HWAVEIN hMic, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    {
    	WAVEHDR whIn;
    	switch(uMsg)
    	{
    	case WIM_OPEN:
    		cout << "WIN_OPEN" << endl;
    		break;
    	case WIM_DATA:
    		cout << "WIM_DATA" << endl;
    		cout << "Bytes grabados: " << ((WAVEHDR*)dwParam1)->dwBytesRecorded << endl;
    		
    		memset(&whIn, 0, sizeof(WAVEHDR));
    		if(bud == 0)
    		{
    			whIn.lpData = (LPSTR) buffer3;
    			bud = 1;
    		}
    		else if(bud == 1)
    		{
    			whIn.lpData = (LPSTR) buffer1;
    			bud = 2;
    		}
    		else
    		{
    			whIn.lpData = (LPSTR) buffer2;
    			bud = 0;
    		}
    		if(((WAVEHDR*)dwParam1)->dwBytesRecorded == 16000)
    			enviado = true;
    		whIn.dwBufferLength = dwTamañoBuffer;
    
                    //Inicio código de pruebas
    
    		cout << "Prueba de escritura al buffer antes de pasarlo: ";
    		((char*)(whIn.lpData))[0] = 'P';
    		((char*)(whIn.lpData))[1] = 'r';
    		((char*)(whIn.lpData))[2] = 'u';
    		((char*)(whIn.lpData))[3] = 'e';
    		((char*)(whIn.lpData))[4] = 'b';
    		((char*)(whIn.lpData))[5] = 'a';
    		((char*)(whIn.lpData))[6] = 0;
    		cout << "Correcto" << endl;
    		cout << "Prueba de lectura al buffer antes de pasarlo: ";
    		if(strcmp(((char*)(whIn.lpData)), "Prueba") == 0)
    			cout << "Correcto" << endl;
    		else
    			cout << "Error" << endl;
    		cout << "Se procede a pasar el buffer" << endl;
    
                    //Fin código de pruebas
    
    		waveInPrepareHeader(hMic, &whIn, sizeof(WAVEHDR));
    		waveInAddBuffer(hMic, &whIn, sizeof(WAVEHDR));
    		break;
    	case WIM_CLOSE:
    		cout << "WIM_CLOSE" << endl;
    		break;
    	}
    }
    

    No quiero críticas sobre la selección del buffer siguiente, cuando inicié el proyecto fue lo primero que me vino a la mente y no decidí modificarlo porque realmente funcionaba muy bien ;)

    Adjunto acá la salida de la consola:

    Línea #1 del módem
    Formatos: 0
    Presione una tecla para continuar . . .
    WIN_OPEN
    Dispositivo abierto
    WIM_DATA
    Bytes grabados: 15996
    Prueba de escritura al buffer antes de pasarlo: Correcto
    Prueba de lectura al buffer antes de pasarlo: Correcto
    Se procede a pasar el buffer
    Presione una tecla para continuar . . . 
    WIM_DATA
    Bytes grabados: 15996
    Prueba de escritura al buffer antes de pasarlo: Correcto
    Prueba de lectura al buffer antes de pasarlo: Correcto
    Se procede a pasar el buffer
    WIM_DATA
    Bytes grabados: 960
    Prueba de escritura al buffer antes de pasarlo: Correcto
    Prueba de lectura al buffer antes de pasarlo: Correcto
    Se procede a pasar el buffer
    WIM_DATA
    Bytes grabados: 273397828
    Prueba de escritura al buffer antes de pasarlo: Correcto
    Prueba de lectura al buffer antes de pasarlo: Correcto
    Se procede a pasar el buffer
    WIM_DATA
    Bytes grabados: 15996
    Prueba de escritura al buffer antes de pasarlo: Correcto
    Prueba de lectura al buffer antes de pasarlo: Correcto
    Se procede a pasar el buffer
    
    //Acá se produce la excepción, cuando se pasa el 5to buffer

    Como se puede ver, no hay problema de escritura ni de lectura cuando se pasa el último buffer, sin embargo, falla.

    Una vez más, gracias por la ayuda.

    martes, 22 de enero de 2013 19:32
  • Ok, perdón por la tardanza.

    No tengo experiencia con las funciones de audio en Windows así que me cuesta un poco.  Sí veo algo que es potencialmente su problema:  La líena waveInPrepareHeader(hMic, &whIn, sizeof(WAVEHDR)); en el case WIM_DATA (y la siguiente línea).  La variable whIn es una variable en el stack y usted está usándola para una función asíncrona.  Muy malo.  En el momento en que la función waveInCallback() acabe dicha variable es destruida.  Usted DEBE crear el WAVEHDR en el heap dinámicamente para asegurarse que el header es válido todo el tiempo (y por supuesto destruirlo cuando ya no es necesario).

    Si arregla eso, arreglará su problema.  99.99% seguro.


    Jose R. MCP
    Code Samples

    • Propuesto como respuesta webJoseModerator domingo, 3 de febrero de 2013 2:45
    • Marcado como respuesta Mr. Hell domingo, 3 de febrero de 2013 19:14
    domingo, 3 de febrero de 2013 2:45
    Moderador
  • Efectivamente, ese era el problema, lo solucioné agregando un [HeapAlloc()] antes de iniciar la grabación y haciendo global whIn. Ahora me surge un par de preguntas; la primera: al intentar liberar la memoria con [HeapFree()] al salir del programa se produce una excepción diciendo que un montón está dañado, la cual no se produce si no libero la memoria; la segunda pregunta: ¿Por qué este error se producía al recibir sonido del módem, pero no cuando recibía sonido de la placa de sonido, si se usaba exactamente el mismo código?

    Bueno, muchas gracias por la ayuda, y si me pueden responder las preguntas, se los agradecería.

    domingo, 3 de febrero de 2013 19:17
  • Sigue siendo mala idea usar una única variable de tipo WAVEHDR.  Yo no sé este tema de audio pero pienso que si es multihilo (que lo es), existe la posibilidad de ocupar más de un WAVEHDR a la vez.  Por ejemplo, mientras usted está procesando un búfer puede ser que la función de audio esté usando otro, pero oh oh!, es el mismo WAVEHDR para ambos.  No me parece para NADA bueno.

    Yo usaría el operador new o HeapAlloc() (al final es lo mismo) para crear el WAVEHDR en el heap y lo destruiría cada vez que recibo el búfer lleno.

    Recuerde que las funciones de memoria en modo Debug suelen agregar búferes adicionales antes y después de la asignación de RAM con valores especiales para así determinar si hubo corrupción de datos debido a overruns.  Me suena a que este es su caso y por eso mejor le recomiendo abortar el uso de un único WAVEHDR global.

    ¿Por qué este error se producía al recibir sonido del módem, pero no cuando recibía sonido de la placa de sonido, si se usaba exactamente el mismo código?

    Tendría usted que preguntarle a un ingeniero de Microsoft o al ingeniero que creó los controladores de sonido de su placa de sonido.


    Jose R. MCP
    Code Samples

    domingo, 3 de febrero de 2013 20:11
    Moderador