Compartir a través de


Información general sobre la interoperabilidad de código administrado y no administrado

 

Sonja Keserovic, gerente del programa
David Mortenson, ingeniero de diseño de software principal
Adam Nathan, ingeniero de diseño de software principal en pruebas

Microsoft Corporation

Octubre de 2003

Se aplica a:
   Microsoft® .NET Framework
   Interoperabilidad COM

Resumen: En este artículo se proporcionan hechos básicos sobre la interoperabilidad entre código administrado y no administrado, así como instrucciones y prácticas comunes para acceder a la API no administrada desde código administrado y para exponer las API administradas a los autores de llamadas no administrados. También se resaltan las consideraciones de seguridad y confiabilidad, los datos de rendimiento y las prácticas generales para los procesos de desarrollo. (14 páginas impresas)

Requisitos previos: El público objetivo de este documento incluye desarrolladores y administradores que necesitan tomar decisiones de alto nivel sobre dónde usar el código administrado. Para ello, resulta útil comprender cómo funciona la interacción entre código administrado y no administrado y cómo se aplican las directrices actuales a escenarios específicos.

Contenido

Introducción a la interoperabilidad
Instrucciones de interoperabilidad
Seguridad
Confiabilidad
Rendimiento
Apéndice 1: Cruce del límite de interoperabilidad
Apéndice 2: Recursos
Apéndice 3: Glosario de términos

Introducción a la interoperabilidad

Common Language Runtime (CLR) promueve la interacción del código administrado con componentes COM, servicios COM+, la API win32® y otros tipos de código no administrado. Los tipos de datos, los mecanismos de control de errores, las reglas de creación y destrucción, y las directrices de diseño varían entre los modelos de objetos administrados y no administrados. Para simplificar la interoperación entre código administrado y no administrado y para facilitar la ruta de migración, la capa de interoperabilidad de CLR oculta las diferencias entre estos modelos de objetos de los clientes y los servidores.

La interoperabilidad ("interoperabilidad") es bidireccional, lo que permite:

  • Llamada a api no administradas desde código administrado

    Esto se puede hacer para las API planas (exportaciones de DLL estáticas, como la API de Win32, que se expone desde archivos DLL como kernel32.dll y user32.dll) y API COM (modelos de objetos como los expuestos por Microsoft® Word, Excel, Internet Explorer, Objetos de datos ActiveX® (ADO), etc.

  • Exposición de API administradas a código no administrado

    Algunos ejemplos de esto incluyen la creación de un complemento para una aplicación basada en COM, como el Reproductor de Windows Media® o la inserción de un control de Windows Forms administrado en un formulario MFC.

Tres tecnologías complementarias permiten estas interacciones administradas o no administradas:

  • La invocación de plataforma (a veces denominada P/Invoke) permite llamar a cualquier función en cualquier lenguaje no administrado siempre que se vuelva a declarar su firma en el código fuente administrado. Esto es similar a la funcionalidad proporcionada por la Declare instrucción en Visual Basic® 6.0.
  • La interoperabilidad COM permite llamar a componentes COM en cualquier lenguaje administrado de forma similar al uso de componentes administrados normales y viceversa. La interoperabilidad COM se compone de servicios principales proporcionados por CLR, además de algunas herramientas y API en el espacio de nombres System.Runtime.InteropServices .
  • La interoperabilidad de C++ (a veces denominada It Just Works (IJW)) es una característica específica de C++, que permite usar api planas y API COM directamente, ya que siempre se han usado. Esto es más eficaz que la interoperabilidad COM, pero requiere mucho más cuidado. Asegúrese de comprobar los recursos de C++ antes de usar esta tecnología.

Instrucciones de interoperabilidad

Llamada a api no administradas desde código administrado

Hay varios tipos de API no administradas y varios tipos de tecnologías de interoperabilidad disponibles para llamar a ellas. En esta sección se describen sugerencias sobre cómo y cuándo usar estas tecnologías. Tenga en cuenta que estas sugerencias son muy generales y no cubren todos los escenarios. Debe evaluar cuidadosamente los escenarios y aplicar prácticas de desarrollo o soluciones que tengan sentido para su escenario.

Llamada a LAS API planas no administradas

Hay dos mecanismos para llamar a api planas no administradas desde código administrado: mediante la invocación de plataforma (disponible en todos los lenguajes administrados) o la interoperabilidad de C++ (disponible en C++).

Antes de decidir llamar a una API plana mediante cualquiera de estas tecnologías de interoperabilidad, debe determinar si hay una funcionalidad equivalente disponible en .NET Framework. Se recomienda que, siempre que sea posible, use la funcionalidad de .NET Framework en lugar de llamar a las API no administradas.

Para llamar a solo algunos métodos no administrados o para llamar a API planas simples, la sugerencia es usar la invocación de plataforma en lugar de la interoperabilidad de C++. Escribir declaraciones de invocación de plataforma para API planas sencillas es sencilla. CLR se encargará de la carga de DLL y de todas las referencias de parámetros. Incluso el trabajo de escribir algunas declaraciones de invocación de plataforma para API planas complejas es insignificante en comparación con el costo de usar la interoperabilidad de C++ e introducir un nuevo módulo completo escrito en C++.

Para encapsular API planas complejas no administradas o para encapsular API planas no administradas que cambian mientras el código administrado está en desarrollo, la sugerencia es usar la interoperabilidad de C++ en lugar de invocar a la plataforma. La capa de C++ puede ser muy delgada y el resto del código administrado se puede escribir en cualquier otro lenguaje administrado que prefiera. El uso de la invocación de plataforma en estos escenarios requeriría mucho esfuerzo para volver a declarar partes complejas de la API en código administrado y mantenerlos sincronizados con las API no administradas. El uso de la interoperabilidad de C++ resuelve este problema al permitir el acceso directo a las API no administradas, lo que no requiere reescritura, solo la inclusión de un archivo de encabezado.

Llamada a LAS API COM

Hay dos maneras de llamar a componentes COM desde código administrado: a través de la interoperabilidad COM (disponible en todos los lenguajes administrados) o a través de la interoperabilidad de C++ (disponible en C++).

Para llamar a componentes COM compatibles con OLE Automation, la sugerencia es usar la interoperabilidad COM. CLR se encargará de la activación de componentes COM y de la serialización de parámetros.

Para llamar a componentes COM basados en el lenguaje de definición de interfaz (IDL), la sugerencia es usar la interoperabilidad de C++. La capa de C++ puede ser muy delgada y el resto del código administrado se puede escribir en cualquier lenguaje administrado. La interoperabilidad COM se basa en la información de las bibliotecas de tipos para realizar llamadas de interoperabilidad correctas, pero las bibliotecas de tipos normalmente no contienen toda la información presente en los archivos IDL. El uso de la interoperabilidad de C++ resuelve este problema al permitir el acceso directo a estas API COM.

En el caso de las empresas que poseen API COM que ya se han enviado, es importante considerar la posibilidad de enviar ensamblados de interoperabilidad primarios (PIA) para estas API, lo que facilita su consumo para los clientes administrados.

Árbol de decisión para llamar a API no administradas

Figura 1. Llamar al árbol de decisión de las API no administradas

Exposición de las API administradas al código no administrado

Hay dos maneras principales de exponer una API administrada a autores de llamadas puramente no administrados: como UNA API COM o como API plana. En el caso de los clientes no administrados de C++ que están dispuestos a volver a compilar su código con Visual Studio® .NET, hay una tercera opción: acceder directamente a la funcionalidad administrada a través de la interoperabilidad de C++. En esta sección se describen sugerencias sobre cómo y cuándo usar estas opciones.

Acceso directo a una API administrada

Si un cliente no administrado está escrito en C++, se puede compilar con el compilador de C++ de Visual Studio .NET como una "imagen de modo mixto". Una vez hecho esto, el cliente no administrado puede acceder directamente a cualquier API administrada. Sin embargo, algunas reglas de codificación se aplican al acceso a objetos administrados desde código no administrado; Consulte la documentación de C++ para obtener más detalles.

El acceso directo es la opción preferida, ya que no requiere ninguna consideración especial de los desarrolladores de API administradas. Pueden diseñar su API administrada según las directrices de diseño de API administradas (DG) y estar seguros de que la API seguirá siendo accesible para los autores de llamadas no administrados.

Exposición de una API administrada como UNA API COM

Cada clase administrada pública se puede exponer a clientes no administrados a través de la interoperabilidad COM. Este proceso es muy fácil de implementar, ya que la capa de interoperabilidad COM se encarga de todas las tuberías COM. Por lo tanto, por ejemplo, cada clase administrada parece implementar IUnknown, IDispatch, ISupportErrorInfo y algunas otras interfaces COM estándar.

A pesar de que exponer las API administradas como API COM es fácil, los modelos de objetos COM y administrados son muy diferentes. Por lo tanto, exponer la API administrada a COM siempre debe ser una decisión de diseño explícita. Algunas características disponibles en el mundo administrado no tienen ningún equivalente en el mundo COM y no se podrán usar desde clientes COM. Por este motivo, a menudo hay tensión entre las directrices de diseño de API administradas (DG) y la compatibilidad con COM.

Si los clientes COM son importantes, escriba la API administrada según las directrices de diseño de la API administrada y, a continuación, escriba un contenedor administrado ligero compatible con COM en torno a la API administrada que se expondrá a COM.

Exposición de una API administrada como API plana

A veces, los clientes no administrados no pueden usar COM. Por ejemplo, es posible que ya se escriban para usar API planas y no se pueden cambiar ni volver a compilar. C++ es el único lenguaje de alto nivel que permite exponer las API administradas como API planas. Esto no es tan sencillo como exponer una API administrada como UNA API COM. Es una técnica muy avanzada que requiere conocimientos avanzados de interoperabilidad de C++ y las diferencias entre los mundos administrados y no administrados.

Exponga la API administrada como API plana solo si es absolutamente necesario. Si no tiene ninguna opción, asegúrese de comprobar la documentación de C++ y tenga en cuenta todas las limitaciones.

Árbol de decisión para exponer las API administradas

Ilustración 2. Exposición del árbol de decisión de las API administradas

Seguridad

Common Language Runtime se distribuye con un sistema de seguridad, Seguridad de acceso a código (CAS), que regula el acceso a los recursos protegidos en función de la información sobre el origen de un ensamblado. Llamar a código no administrado presenta un riesgo de seguridad importante. Sin las comprobaciones de seguridad adecuadas, el código no administrado podría manipular cualquier estado de cualquier aplicación administrada en el proceso clR. También es posible llamar directamente a recursos en código no administrado, sin que estos recursos estén sujetos a comprobaciones de permisos CAS. Por ese motivo, cualquier transición al código no administrado se considera una operación altamente protegida y debe incluir una comprobación de seguridad. Esta comprobación de seguridad busca el permiso de código no administrado que requiere el ensamblado que contiene la transición de código no administrado, así como todos los ensamblados que llaman a él, para tener derecho a invocar realmente código no administrado.

Hay algunos escenarios de interoperabilidad limitados en los que las comprobaciones de seguridad completas no son necesarias y limitarían indebidamente el rendimiento o el ámbito del componente. Este es el caso si un recurso expuesto desde código no administrado no tiene relevancia de seguridad (tiempo del sistema, coordenadas de ventana, etc.), o el recurso solo se usa internamente en el ensamblado y no se expone públicamente a autores de llamadas arbitrarios. En tales casos, puede suprimir la comprobación de seguridad completa para el permiso de código no administrado en todos los autores de llamadas de las API pertinentes. Para ello, aplique el atributo personalizado SuppressUnmanagedCodeSecurity al método o clase de interoperabilidad respectivos. Tenga en cuenta que esto supone una revisión de seguridad cuidadosa en la que ha determinado que ningún código de confianza parcial podría aprovechar dichas API.

Confiabilidad

El código administrado está diseñado para ser más confiable y sólido que el código no administrado. Un ejemplo de una característica CLR que promueve estas cualidades es la recolección de elementos no utilizados, que se encarga de liberar memoria sin usar para evitar pérdidas de memoria. Otro ejemplo es la seguridad de tipos administrados, que se usa para evitar errores de saturación del búfer y otros errores relacionados con el tipo.

Cuando se usa cualquier tipo de tecnología de interoperabilidad, es posible que el código no sea tan confiable o sólido como código administrado puro. Por ejemplo, es posible que tenga que asignar memoria no administrada manualmente y recordar liberarla cuando haya terminado con ella.

La escritura de cualquier código de interoperabilidad no trivial requiere la misma atención a la confiabilidad y la solidez que la escritura de código no administrado. Incluso cuando todo el código de interoperabilidad está escrito correctamente, el sistema solo será tan confiable como sus partes no administradas.

Rendimiento

Con cada transición del código administrado al código no administrado (y viceversa), hay cierta sobrecarga de rendimiento. La cantidad de sobrecarga depende de los tipos de parámetros usados. La capa de interoperabilidad de CLR usa tres niveles de optimización de llamadas de interoperabilidad en función del tipo de transición y los tipos de parámetros: inserción Just-In-Time (JIT), codificación de códigos auxiliares de ensamblado compilados y códigos auxiliares de serialización interpretados (en orden de más rápido a más lento tipo de llamada).

Sobrecarga aproximada para una llamada de invocación de plataforma: 10 instrucciones de máquina (en un procesador x86)

Sobrecarga aproximada para una llamada de interoperabilidad COM: 50 instrucciones de máquina (en un procesador x86)

El trabajo realizado por estas instrucciones se muestra en las secciones del apéndice Llamada a una API plana: Paso a paso y Llamada a una API COM: Paso a paso. Además de asegurarse de que el recolector de elementos no utilizados no bloqueará los subprocesos no administrados durante la llamada y controlar las convenciones de llamada y las excepciones no administradas, la interoperabilidad COM realiza un trabajo adicional para convertir la llamada en el contenedor actual al que se puede llamar (RCW) en un puntero de interfaz COM adecuado para el contexto actual.

Cada llamada de interoperabilidad presenta cierta sobrecarga. Dependiendo de la frecuencia con la que se produzcan estas llamadas y la importancia del trabajo que se produce dentro de la implementación del método, la sobrecarga por llamada puede oscilar entre insignificante y muy notable.

En función de estas consideraciones, la lista siguiente proporciona algunas sugerencias generales de rendimiento que puede resultar útil:

  • Si controla la interfaz entre código administrado y no administrado, haga que sea "fragmentado" en lugar de "chatty", para reducir el número total de transiciones realizadas.

    Las interfaces chatty son interfaces que realizan muchas transiciones sin realizar ningún trabajo significativo en el otro lado del límite de interoperabilidad. Por ejemplo, los establecedores de propiedades y los captadores son chatty. Las interfaces fragmentadas son interfaces que realizan solo algunas transiciones y la cantidad de trabajo realizado en el otro lado del límite es significativa. Por ejemplo, un método que abre una conexión de base de datos y recupera algunos datos es fragmentado. Las interfaces fragmentadas implican menos transiciones de interoperabilidad, por lo que se elimina cierta sobrecarga de rendimiento.

  • Evite las conversiones Unicode/ANSI si es posible.

    La conversión de cadenas de Unicode a ANSI y viceversa es una operación costosa. Por ejemplo, si es necesario pasar cadenas, pero su contenido no es importante, puede declarar un parámetro de cadena como intPtr y el serializador de interoperabilidad no realizará ninguna conversión.

  • En escenarios de alto rendimiento, declarar parámetros y campos como IntPtr puede aumentar el rendimiento, aunque a costa de la facilidad de uso y la facilidad de mantenimiento.

    A veces es más rápido realizar serialización manual mediante métodos disponibles en la clase Marshal , en lugar de basarse en las referencias de interoperabilidad predeterminadas. Por ejemplo, si se deben pasar matrices grandes de cadenas a través de un límite de interoperabilidad, pero solo se necesitarán algunos elementos, declarar la matriz como intPtr y acceder a solo esos pocos elementos manualmente será mucho más rápido.

  • Use InAttribute y OutAttribute sabiamente para reducir las serialización innecesarias.

    El serializador de interoperabilidad usa reglas predeterminadas al decidir si un parámetro determinado debe serializarse en antes de la llamada y serializarse después de la llamada. Estas reglas se basan en el nivel de direccionamiento indirecto y el tipo de parámetro. Es posible que algunas de estas operaciones no sean necesarias en función de la semántica del método.

  • Use SetLastError=false en la plataforma invoca firmas solo si llamará a Marshal.GetLastWin32Error después.

    Establecer SetLastError=true en las firmas de invocación de plataforma requiere trabajo adicional de la capa de interoperabilidad para conservar el último código de error. Use esta característica solo cuando se base en esta información y la usará después de realizar la llamada.

  • Si, y solo si, las llamadas no administradas se exponen de forma no aprovechable, use SuppressUnmanagedCodeSecurityAttribute para reducir el número de comprobaciones de seguridad.

    Las comprobaciones de seguridad son muy importantes. Si la API no expone ningún recurso protegido o información confidencial, o bien están bien protegidos, es posible que las comprobaciones de seguridad exhaustivas introduzcan una sobrecarga innecesaria. Sin embargo, el costo de no realizar ninguna comprobación de seguridad es muy alto.

Apéndice 1: Cruzar el límite de interoperabilidad

Llamada a una API plana: paso a paso

Figura 3. Llamada a una API plana

  1. Obtenga LoadLibrary y GetProcAddress.
  2. Compile un código auxiliar DllImport a partir de la firma que contiene la dirección de destino.
  3. Insertar registros guardados por destinatarios.
  4. Configure un marco DllImport y insértelo en la pila de marcos.
  5. Si se asigna memoria temporal, inicialice una lista de limpieza para liberarse rápidamente cuando se complete la llamada.
  6. Serializar parámetros. (Esto podría asignar memoria).
  7. Cambie el modo recolección de elementos no utilizados de cooperativa a preferente, por lo que una recolección de elementos no utilizados puede producirse en cualquier momento.
  8. Cargue la dirección de destino y llámala.
  9. Si se establece el bit SetLastError , llame a GetLastError y almacene el resultado en una abstracción de subproceso almacenada en El almacenamiento local de subprocesos.
  10. Vuelva al modo de recolección de elementos no utilizados cooperativa.
  11. Si PreserveSig=false y el método devolvió un error HRESULT, inicie una excepción.
  12. Si no se produjo ninguna excepción, propague los parámetros back-propagate y by-ref .
  13. Restaure el puntero de pila extendida a su valor original para tener en cuenta los argumentos extraídos por el autor de la llamada.

Llamar a una API COM: Paso a paso

Figura 4. Llamada a una API COM

  1. Cree un código auxiliar administrado a no administrado a partir de la firma.
  2. Insertar registros guardados por destinatarios.
  3. Configure un marco de interoperabilidad COM administrado a no administrado e insértelo en la pila de fotogramas.
  4. Reserve espacio para los datos temporales utilizados durante la transición.
  5. Si se asigna memoria temporal, inicialice una lista de limpieza para liberarse rápidamente cuando se complete la llamada.
  6. Borre las marcas de excepción de punto flotante (solo x86).
  7. Serializar parámetros. (Esto podría asignar memoria).
  8. Recupere el puntero de interfaz correcto para el contexto actual dentro del contenedor invocable en tiempo de ejecución. Si no se puede usar el puntero almacenado en caché, llame a QueryInterface en el componente COM para obtenerlo.
  9. Cambie el modo recolección de elementos no utilizados de cooperativa a preferente, por lo que una recolección de elementos no utilizados puede producirse en cualquier momento.
  10. Desde el puntero de la tabla virtual, indíquelo por el número de ranura, obtenga la dirección de destino y llámelo.
  11. Llame a Release en el puntero de interfaz si se llamó anteriormente a QueryInterface .
  12. Vuelva al modo de recolección de elementos no utilizados cooperativa.
  13. Si la firma no está marcada como PreserveSig, compruebe si hay un error HRESULT y genere una excepción (posiblemente rellenada con información de IErrorInfo ).
  14. Si no se produjo ninguna excepción, propague los parámetros back-propagate y by-ref .
  15. Restaure el puntero de pila extendida al valor original para tener en cuenta los argumentos extraídos por el autor de la llamada.

Llamada a una API administrada desde COM: paso a paso

Figura 5. Llamada a una API administrada desde COM

  1. Compile un código auxiliar no administrado a administrado a partir de la firma.
  2. Insertar registros guardados por destinatarios.
  3. Configure un marco de interoperabilidad COM no administrado a administrado e insértelo en la pila de marcos.
  4. Reserve espacio para los datos temporales utilizados durante la transición.
  5. Cambie el modo recolección de elementos no utilizados de cooperativa a preferente para que se pueda producir una recolección de elementos no utilizados en cualquier momento.
  6. Recupere el contenedor al que se puede llamar COM (CCW) desde el puntero de interfaz.
  7. Recupere el objeto administrado dentro del CCW.
  8. Realice la transición de appdomains, si es necesario.
  9. Si un appdomain no tiene plena confianza, realice las demandas de vínculo que el método pueda tener en el appdomain de destino.
  10. Si se asigna memoria temporal, inicialice una lista de limpieza para liberarse rápidamente cuando se complete la llamada.
  11. Serializar parámetros. (Esto podría asignar memoria).
  12. Busque el método administrado por destino que se va a llamar. (Esto implica asignar llamadas de interfaz a la implementación de destino).
  13. Almacene en caché el valor devuelto. (Si es un valor devuelto de punto flotante, obtengalo del registro de punto flotante).
  14. Vuelva al modo de recolección de elementos no utilizados cooperativa.
  15. Si se produjo una excepción, extraiga su HRESULT para devolver y llame a SetErrorInfo.
  16. Si no se produjo ninguna excepción, propague los parámetros back-propagate y by-ref .
  17. Restaure el puntero de pila extendida al valor original para tener en cuenta los argumentos extraídos por el autor de la llamada.

Apéndice 2: Recursos

¡Debe leer!.NET y COM: Guía completa de interoperabilidad de Adam Nathan

Interoperación con código no administrado, Guía del desarrollador de Microsoft .NET Framework

Ejemplos de interoperabilidad, Microsoft .NET Framework

Blog de Adam Nathan

Blog de Chris Brumme

Apéndice 3: Glosario de términos

AppDomain (dominio de aplicación) Un dominio de aplicación se puede considerar similar a un proceso ligero del sistema operativo y se administra mediante Common Language Runtime.
CCW (contenedor al que se puede llamar COM) Un tipo especial de contenedor creado por la capa de interoperabilidad clR alrededor de los objetos administrados activados desde código COM. Un CCW oculta las diferencias entre los modelos de objetos administrados y COM al proporcionar referencias de datos, administración de la duración, administración de identidades, control de errores, transiciones de apartamento y subprocesos correctas, etc. Los CCW exponen la funcionalidad de objetos administrados de una manera fácil de usar COM sin necesidad de que el implementador de código administrado conozca nada sobre la canalización COM.
CLR Common Language Runtime.
Interoperabilidad COM El servicio proporcionado por la capa de interoperabilidad de CLR para usar API COM desde código administrado o exponer las API administradas como API COM a clientes no administrados. La interoperabilidad COM está disponible en todos los lenguajes administrados.
Interoperabilidad de C++ El servicio proporcionado por el compilador del lenguaje C++ y CLR, se usa para mezclar directamente código administrado y no administrado en el mismo archivo ejecutable. La interoperabilidad de C++ normalmente implica incluir archivos de encabezado en API no administradas y seguir ciertas reglas de codificación.
API plana compleja API que tienen firmas que son difíciles de declarar en lenguaje administrado. Por ejemplo, los métodos con parámetros de estructura de tamaño variable son difíciles de declarar, ya que no hay ningún concepto equivalente en el sistema de tipos administrados.
Interop El término general que abarca cualquier tipo de interoperabilidad entre código administrado y no administrado (también denominado "nativo"). La interoperabilidad es uno de los muchos servicios proporcionados por CLR.
Ensamblado de interoperabilidad Tipo especial de ensamblado administrado que contiene equivalentes de tipo administrado para los tipos COM contenidos en una biblioteca de tipos. Normalmente se genera mediante la ejecución de la herramienta Importador de biblioteca de tipos (Tlbimp.exe) en una biblioteca de tipos.
Código administrado El código que se ejecuta bajo el control de CLR se denomina código administrado. Por ejemplo, cualquier código escrito en C# o Visual Basic .NET es código administrado.
Invocación de plataforma El servicio proporcionado por la capa de interoperabilidad de CLR para llamar a las API planas no administradas desde código administrado. La invocación de plataforma está disponible en todos los lenguajes administrados.
RCW (wapper invocable en tiempo de ejecución) Un tipo especial de contenedor creado por la capa de interoperabilidad clR alrededor de objetos COM que se activan desde código administrado. Una RCW oculta las diferencias entre los modelos de objetos administrados y COM al proporcionar referencias de datos, administración de la duración, administración de identidades, control de errores, transiciones de apartamento y subprocesos correctas, etc.
Código no administrado El código que se ejecuta fuera de CLR se conoce como "código no administrado". Los componentes COM, los componentes ActiveX y las funciones de API de Win32 son ejemplos de código no administrado.