Compartir a través de


Insertar código dinámicamente con la API de depuración

En esta sección se describe la inyección dinámica de código utilizando la API de depuración de Common Language Runtime (CLR). La inyección dinámica de código ejecuta una función que no estaba presente en el archivo ejecutable portable (PE) original. Por ejemplo, puede utilizar la inyección dinámica de código para ejecutar una expresión en la ventana Inmediato mientras está depurando en el entorno de desarrollo integrado (IDE) de Microsoft Visual Studio. CLR secuestra un subproceso activo para ejecutar el código. El depurador puede solicitar a CLR que ejecute o inmovilice los subprocesos restantes. Dado que la inyección dinámica de código dinámica se compila al evaluar la función, es posible depurar una función insertada dinámicamente como si fuera código normal. Es decir, puede invocar todos los servicios de depuración estándar, tales como el establecimiento de puntos de interrupción, el recorrido paso a paso, etc. para el código insertado dinámicamente.

Un depurador debe hacer lo siguiente para insertar código dinámicamente:

  • Cree una función que ejecute el código dinámico.

  • Inserte el código en el código que está siendo depurado utilizando Editar y continuar.

  • Ejecute el código, repetidamente si los desea, invocando la función creada.

En las subsecciones siguientes se describen estos pasos más detalladamente.

Crear la función

  1. Calcular la firma para la función que ejecutará el código dinámico. Para ello, anexe la firma para las variables locales a la firma para el método que se está ejecutando en el marco de hoja. Una firma construida de esta manera no requiere ningún cálculo del subconjunto mínimo de variables utilizadas. El tiempo de ejecución omitirá las variables no usadas. Vea "Obtener la firma de los metadatos" más adelante en este tema para obtener más información.

    Todos los argumentos a la función se deben declarar para que sean ByRef. Esto permitirá a la evaluación de la función propagar de nuevo los cambios de las variables en la función insertada al marco de hoja en el código que está siendo depurado.

    Es posible que algunas variables no estén en ámbito cuando se ejecute el código dinámico. En tales situaciones, debe pasarse una referencia null. Si hace referencia a una variable fuera de ámbito, se iniciará una NullReferenceException. Cuando esto ocurra, el depurador puede completar la evaluación de la función llamando a la devolución de llamada ICorDebugManagedCallback::EvalException.

  2. Elija un nombre único para la función. Debe prefijar el nombre de función con la cadena "_Hidden", de la cadena: para que el depurador impida que los usuarios examinen la función. Cuando se agrega esta función, se establece un marcador que indica que el nombre de función es especial.

Insertar el código

  1. El depurador debe pedir al compilador que genere una función cuyo cuerpo sea el código que se insertará dinámicamente.

  2. El depurador calcula un archivo ejecutable portable (PE) delta. Para ello, el depurador llama al método ICorDebugModule::GetEditAndContinueSnapshot para obtener un objeto ICorDebugEditAndContinueSnapshot. El depurador llama a métodos ICorDebugEditAndContinueSnapshot para crear la imagen PE delta y a ICorDebugController::CommitChanges para instalar el PE delta en la imagen en ejecución.

    La función insertada dinámicamente debe colocarse en el mismo nivel de visibilidad que el marco de hoja en el que ejecutará. Si el marco de hoja es un método de instancia, la función insertada dinámicamente también debe ser un método de instancia de la misma clase. Si el marco de hoja es un método estático, la función insertada dinámicamente también debe ser un método estático. Los métodos globales son métodos estáticos que pertenecen a una clase determinada.

    NotaNota

    La función incluso existirá en el código que está siendo depurado después de que se complete el proceso de inserción del código dinámico.Esto permite reevaluar repetidamente el código previamente insertado sin tener que recomponer la función e insertar de nuevo el código.Por consiguiente, los pasos descritos en esta sección y en la sección anterior pueden omitirse para el código previamente insertado.

Ejecutar código insertado

  1. Obtener el valor (un objeto ICorDebugValue) de cada variable de la firma usando las rutinas de inspección de depuración. Puede utilizar el método ICorDebugThread::GetActiveFrame o el método ICorDebugChain::GetActiveFrame para obtener el marco de la hoja y QueryInterface para ICorDebugILFrame. Llame al método ICorDebugILFrame::EnumerateLocalVariables, ICorDebugILFrame::EnumerateArguments, ICorDebugILFrame::GetLocalVariable o ICorDebugILFrame::GetArgument para obtener las variables reales.

    NotaNota

    Si el depurador está asociado a un código que está siendo depurado y no tiene establecido CORDBG_ENABLE (es decir, un código que está siendo depurado y que no ha estado recolectando información de depuración), el depurador no podrá obtener un objeto ICorDebugILFrame y, por consiguiente, no podrá recopilar valores para la evaluación de la función.

    No es importante si los objetos que son argumentos para la función insertada dinámicamente se desreferencian de forma débil o fuerte. Cuando se evalúe la función, el tiempo de ejecución secuestrará el subproceso en el que se produjo la inserción. Esto dejará el marco de la hoja original y todas las referencias fuertes originales en la pila. Sin embargo, si todas las referencias en el código que está siendo depurado son débiles, al ejecutar la inserción dinámica de código, se puede activar una recolección de elementos no utilizados, que puede hacer que el objeto se recolecte como elemento no utilizado.

  2. Use el método ICorDebugThread::CreateEval para crear un objeto ICorDebugEval. ICorDebugEval proporciona métodos para evaluar una función. Llame a uno de estos métodos. Un método tal como ICorDebugEval::CallFunction solamente prepara la evaluación de la función. El depurador debe llamar a ICorDebugController::Continue para ejecutar el código que está siendo depurado y evaluar la función. Cuando la evaluación se ha completado, los servicios de depuración llaman al método ICorDebugManagedCallback::EvalComplete o ICorDebugManagedCallback::EvalException para notificar al depurador la evaluación de la función.

    Si la evaluación de la función devuelve un objeto, la referencia al objeto será fuerte.

    Si el código insertado dinámicamente intenta desreferenciar una referencia null que se pasa a la función que envuelve el código, los servicios de depuración de CLR llamarán a ICorDebugManagedCallback::EvalException. En respuesta, el depurador puede notificar al usuario que no puede evaluar el código insertado.

    Observe que el método ICorDebugEval::CallFunction no hace envíos virtuales; utilice en su lugar ICorDebugObjectValue::GetVirtualMethod si desea envíos virtuales.

    Si el código que está siendo depurado es multiproceso y el depurador no desea que se ejecute que ninguno de los demás subprocesos, el depurador debe llamar a ICorDebugController::SetAllThreadsDebugState y establecer el estado de todos los subprocesos a THREAD_SUSPEND, excepto para el subproceso que se utiliza para la evaluación de la función. Esta configuración puede producir un interbloqueo, dependiendo de lo que haga el código insertado dinámicamente.

Problemas diversos

  • Dado que la seguridad de .NET Framework está determinada por directivas de contexto, la inserción de dinámica de código funcionará con los mismos permisos de seguridad y funciones que el marco de hoja, a menos que cambie específicamente la configuración de seguridad.

  • Se puede agregar funciones insertadas dinámicamente dondequiera que la característica de Editar y continuar permita agregar funciones. Una opción lógica es agregarlos al marco de hoja.

  • No hay ningún límite para el número de campos, métodos de instancia o métodos estáticos que puede agregar a una clase utilizando operaciones de Editar y continuar. La cantidad máxima de datos estáticos permitida está predefinida y actualmente limitada a 1 MB por módulo.

  • Las instrucciones goto no locales no se permiten en código insertado dinámicamente.

Obtener la firma de los metadatos

Obtener un dispensador de metadatos

Buscar el método utilizando IMetaDataImport

Construir la firma de la función

El formato de la firma se documenta en la especificación "Codificación de tipo y firma en metadatos" y tiene prioridad sobre la información de esta introducción.

La sección "Declaración de método" de la especificación describe el formato para la firma del método. El formato es un byte único para la convención de llamada, seguido por un byte único para el recuento de argumentos, seguido por una lista de tipos. Cada tipo puede tener un tamaño diferente. Si se especifica una convención de llamada de argumento variable, el recuento de argumentos será el número total de argumentos, es decir, argumentos fijos más argumentos variables. Un byte ELEMENT_TYPE_SENTINEL marca dónde finalizan los argumentos fijos y dónde comienzan los variables.

La sección "Firmas independientes" de la especificación describe el formato de las firmas locales. Las firmas independientes no utilizan una convención de llamada de argumento variable.

Después de obtener la firma del método y la firma local, el depurador debe asignar espacio para una nueva firma. El depurador debe recorrer entonces en iteración la firma del método. Para cada tipo, debe colocar el byte ELEMENT_TYPE_BYREF seguido por el tipo en la nueva firma. El proceso debe repetirse hasta alcanzar la firma de fin de método o un tipo marcado como ELEMENT_TYPE_SENTINEL. A continuación, el depurador debe copiar los tipos de firma locales y marcar cada tipo como ELEMENT_TYPE_BYREF. Si la firma del método tiene una convención de llamada de argumento variable, el depurador deber copiar esos tipos y marcarlos como ELEMENT_TYPE_BYREF. Finalmente, el depurador debe actualizar el recuento de argumentos.

Vea también

Conceptos

Información general sobre la depuración en CLR

Otros recursos

Depuración (Referencia de la API no administrada)