TN041: Migración de MFC/OLE1 para MFC/OLE 2

Cuestiones generales relativas a la migración

Uno de los objetivos de diseño para las clases OLE 2 en MFC 2.5 (y superior) fue conservar gran parte de la misma arquitectura de poner en marcha en MFC 2.0 para soporte OLE 1.0. Como resultado, muchas de las mismas clases OLE 2.0 de MFC siguen existan en esta versión de MFC (COleDocument, COleServerDoc, COleClientItem, COleServerItem). Además, muchas de las API en estas clases son exactamente iguales. Sin embargo, OLE 2 es drásticamente diferente de OLE 1.0, por lo que se puede esperar que algunos de los detalles han cambiado. Si está familiarizado con el apoyo de OLE1 MFC 2.0, se sentirá en casa con el apoyo de 2.0 de MFC.

Si están teniendo una aplicación MFC/OLE1 existente y agregar funcionalidad OLE 2 a ella, usted debe leer esta nota. Esta nota cubre algunas cuestiones generales usted puede encontrar mientras portar su funcionalidad OLE1 MFC/OLE 2 y, a continuación, discute los problemas descubiertos mientras portar dos aplicaciones incluidas en MFC 2.0: las muestras de MFC OLE OCLIENT y HIERSVR.

Arquitectura documento/vista de MFC es importante

Si su aplicación no utiliza la arquitectura documento/vista de MFC y desea agregar soporte OLE 2 a la aplicación, ahora es el momento de pasar a la vista del documento. Muchos de los beneficios de las clases de MFC OLE 2 sólo se realizan una vez que su aplicación está utilizando la arquitectura incorporada y componentes de MFC.

La implementación de un servidor o un contenedor sin utilizar la arquitectura MFC es posible, pero no recomendado.

Utilice aplicación MFC en lugar de su propio

MFC "enlatados aplicación" clases como CToolBar, CStatusBary CScrollView tienen código caso especial incorporado para soporte OLE 2. Por lo tanto, si puede utilizar estas clases en su aplicación se beneficiará del esfuerzo invertido en ellos para hacerlos OLE consciente. Una vez más, es posible aquí en clases "roll-su propio" para estos fines, pero no se sugiere. Si necesita implementar una funcionalidad similar, el código fuente MFC es una excelente referencia para tratar algunos de los puntos más finos de OLE (especialmente cuando se trata de activación en el lugar).

Examinar el código de ejemplo MFC

Hay un número de muestras MFC que incluyen la funcionalidad de OLE. Cada una de estas aplicaciones implementa OLE desde un ángulo diferente:

HIERSVR - diseñado principalmente para su uso como una aplicación de servidor. Fue incluido en MFC 2.0 como una aplicación MFC/OLE1 y ha sido portado a MFC/OLE 2 y luego extendido tal que implementa muchas características OLE disponibles en OLE 2.

OCLIENT - se trata de una aplicación contenedora independiente, pretende demostrar muchas de las funciones OLE desde un punto de vista de contenedor. También fue portado desde MFC 2.0 y luego se extendió a apoyar muchas de las características más avanzadas de OLE, como formatos de Portapapeles personalizados y vínculos a elementos incrustados.

DRAWCLI - esta aplicación implementa compatibilidad con contenedor OLE mucho como lo hace OCLIENT, excepto que lo hace en el marco de un programa de dibujo orientado a objetos existente. Le muestra cómo puede implementar la compatibilidad con contenedor OLE e integrarlo en la aplicación existente.

SUPERPAD - esta aplicación, así como una aplicación independiente bien, también es un servidor OLE. El soporte de servidor que implementa es bastante minimalista. De particular interés es cómo utiliza servicios OLE Portapapeles para copiar datos al Portapapeles, pero utiliza la funcionalidad integrada en las ventanas "edita" control para implementar la funcionalidad de pegar del Portapapeles. Esto muestra una interesante mezcla de tradicional uso de API de Windows, así como la integración con la API OLE nuevo.

Para obtener más información sobre las aplicaciones de ejemplo, vea la "MFC muestra ayuda".

Estudio de caso: OCLIENT de MFC 2.0

Como se señaló anteriormente, OCLIENT fue incluido en MFC 2.0 y aplicación OLE con MFC/OLE1. A continuación se describen los pasos que esta aplicación se convirtió inicialmente utilizar las clases MFC/OLE 2. Una serie de características se añadieron después de que el puerto inicial se completó para ilustrar mejor las clases MFC/OLE. Estas características no se tratarán aquí; se refieren a la muestra en sí mismo para obtener más información sobre las características avanzadas.

&Notanbsp;  Los errores del compilador y el proceso paso a paso fue creado con Visual C++ 2.0. Mensajes de error específicos y ubicaciones pueden haber cambiado con Visual C++ 4.0, pero la información conceptual sigue siendo válida.

Lo marcha

El enfoque adoptado para la muestra OCLIENT MFC/OLE de puerto es empezar por crearla y corregir los errores del compilador obvio que dará como resultado. Si se toma la muestra OCLIENT de MFC 2.0 y compilarlo en esta versión de MFC, encontrará que no hay que muchos errores para resolver. A continuación se describen los errores en el orden en que se produjeron.

Compilación y corrección de errores

\oclient\mainview.cpp(104): error C2660: 'Dibujar': función no tome 4 parámetros

El primer error refiere a COleClientItem::Draw. En MFC/OLE1 tomó más parámetros que toma la versión MFC/OLE. Los parámetros adicionales a menudo no eran necesarias y normalmente NULL (como en este ejemplo). Esta versión de MFC puede determinar automáticamente los valores de la lpWBounds cuando el CDC que cabe a un metarchivo DC. Además, el parámetro pFormatDC no es necesario ya que el marco construirá uno de los "atributo" DC del pDC pasado en. Para solucionar este problema, simplemente quitar los dos extra NULL parámetros a la llamada de sorteo.

\oclient\mainview.cpp(273): error C2065: 'OLE_MAXNAMESIZE': identificador no declarado
\oclient\mainview.cpp(273): error C2057: espera expresión constante
\oclient\mainview.cpp(280): error C2664: 'CreateLinkFromClipboard': no se puede convertir el parámetro 1 de 'char [1]' a ' enum:: tagOLERENDER '
\oclient\mainview.cpp(286): error C2664: 'CreateFromClipboard': no se puede convertir el parámetro 1 de 'char [1]' a ' enum:: tagOLERENDER '
\oclient\mainview.cpp(288): error C2664: 'CreateStaticFromClipboard': no se puede convertir el parámetro 1 de 'char [1]' a ' enum:: tagOLERENDER '

Los errores anteriores como resultado del hecho de que todas las funciones de COleClientItem::CreateXXXX en MFC/OLE1 requieren pasar un nombre único para representar el elemento. Se trata de un requisito de la API OLE subyacente. Esto no es necesario en MFC/OLE 2 desde OLE 2 no utilizar DDE como el mecanismo subyacente de las comunicaciones (el nombre fue usado en las conversaciones DDE). Para solucionar este problema, puede quitar la función de CreateNewName así como todas las referencias al mismo. Es fácil averiguar lo que cada función MFC/OLE está esperando simplemente por colocar el cursor sobre la llamada y presionando F1 en esta versión.

Otra área que es significativamente diferente es manejo de Portapapeles de OLE 2. Con OLE1, se utiliza el Portapapeles de Windows que API interactúan con el Portapapeles. Con OLE 2 esto se hace con un mecanismo diferente. Las API MFC/OLE1 asumió que el Portapapeles fue abierto antes de copiar un objeto de COleClientItem en el Portapapeles. Esto ya no es necesario y hará que todas las operaciones de portapapeles MFC/OLE a fallar. Mientras edita el código para eliminar las dependencias de CreateNewName, también debe quitar el código que abre y cierra el Portapapeles de Windows.

\oclient\mainview.cpp(332): error C2065: 'AfxOleInsertDialog': identificador no declarado
\oclient\mainview.cpp(332): error C2064: término no evaluar a una función
\oclient\mainview.cpp(344): error C2057: espera expresión constante
\oclient\mainview.cpp(347): error C2039: 'CreateNewObject': no es un miembro de 'CRectItem'

Estos errores como resultado desde el controlador de CMainView::OnInsertObject . Manejo el comando "Insertar nuevo objeto" es otra área donde las cosas han cambiado un poco. En este caso, es más fácil simplemente combinar la aplicación original con la proporcionada por AppWizard para una nueva aplicación de contenedor OLE. De hecho, esta es una técnica que puede aplicar a portar otras aplicaciones. En MFC/OLE1, se muestra el cuadro de diálogo "Insertar objeto" llamando a la función de AfxOleInsertDialog . En esta versión se construye un objeto de diálogo COleInsertObject y llama a DoModal. Además, se crean nuevos elementos OLE con un CLSID en lugar de una cadena de classname. El resultado final debería ser algo como esto

COleInsertDialog dlg;
Si (dlg.DoModal()! = IDOK)
 nbsp;  retorno;

BeginWaitCursor();

CRectItem * pItem = NULL;
INTENTE
{
    / / Crear primero el objeto de C++
    pItem = GetDocument() - > CreateItem();
    ASSERT_VALID(pItem);

/ / Inicializar el elemento de los datos del cuadro de diálogo.
    Si (! dlg.CreateItem(pItem))
        AfxThrowMemoryException();
           / / hará cualquier excepción
    ASSERT_VALID(pItem);
        
    / / ejecuta el objeto si procede
    Si (dlg.GetSelectionType() == COleInsertDialog::createNewItem)
        pItem - > DoVerb(OLEIVERB_SHOW, this);
        
    / / actualización inmediata
    pItem - > UpdateLink();
    pItem - > UpdateItemRectFromServer();
        
    / / establecer la selección al elemento recién insertada
    SetSelection(pItem);
    pItem - > Invalidate();
}
CATCH (CException, e)
{/ / limpiar el elemento
    Si (pItem! = NULL)
        GetDocument() - > DeleteItem(pItem);
            
    AfxMessageBox(IDP_FAILED_TO_CREATE);
}
END_CATCH
    
EndWaitCursor()

&Notanbsp;  Insertar nuevo objeto puede ser diferente para su aplicación):

También es necesario incluir lt;afxodlgs.h > que contiene la declaración de la clase de diálogo COleInsertObject , así como los otros cuadros de diálogo estándar proporcionados por MFC.

\oclient\mainview.cpp(367): error C2065: 'OLEVERB_PRIMARY': identificador no declarado
\oclient\mainview.cpp(367): error C2660: 'DoVerb': función no toman parámetros 1

Estos errores son causados por el hecho de que algunas constantes OLE1 han cambiado en OLE 2, a pesar de que en el concepto son lo mismo. En este caso OLEVERB_PRIMARY ha cambiado a OLEIVERB_PRIMARY. En tanto OLE1 y OLE 2, el verbo principal es usualmente ejecutado por un contenedor cuando el usuario hace doble clic en un elemento.

Además, DoVerb ahora toma un parámetro adicional: un puntero a una vista (CView*). Este parámetro sólo se utiliza para implementar la "Edición Visual" (o en el lugar activación). Por ahora establezca ese parámetro nulo, ya que no están aplicando esta característica en este momento.

Para asegurarse de que el marco nunca intenta en lugar activa, debe reemplazar el siguiente COleClientItem::CanActivate

 BOOL CRectItem::Ca&nActivate()
{
 nbsp;  Return FALSE;
}

\oclient\rectitem.cpp(53): error C2065: 'GetBounds': identificador no declarado
\oclient\rectitem.cpp(53): error C2064: término no evaluar a una función
\oclient\rectitem.cpp(84): error C2065: 'SetBounds': identificador no declarado
\oclient\rectitem.cpp(84): error C2064: término no evaluar a una función

En MFC/OLE1, COleClientItem::GetBounds y SetBounds fueron utilizado para consultar y manipular el alcance de un elemento (los miembros de la izquierda y arriba siempre eran cero). En MFC/OLE 2 más directamente es compatible con COleClientItem::GetExtent y SetExtent, que se ocupan de un tamaño o CSize en su lugar.

El código de su nuevo SetItemRectToServer, y UpdateItemRectFromServer llamadas este aspecto:

BOOL CRectItem::UpdateItemRectFromServer()
{
 nbsp; Assert(m_bTrackServerSize);
   CSize tamaño;
   if (!.GetExtent(&size))
      Return FALSE;    / / en blanco

/ / Mapa de HIMETRIC a coordenadas de pantalla
   {
      CClientDC screenDC(NULL);
      screenDC.SetMapMode(MM_HIMETRIC);
      screenDC.LPtoDP(&size);
   }
   / / acaba de establecer el tamaño del elemento
   Si (m_rect.Size()! = tamaño)
   {
      / / invalidar el tamaño y posición
      Invalidate();
      m_rect.Right = m_rect.left + size.cx;
      m_rect.Bottom = m_rect.top + size.cy;
      / /, así como el nuevo tamaño y posición
      Invalidate();
   }
   Devuelve TRUE;
}

BOOL CRectItem::SetItemRectToServer()
{
   / / establecer los límites oficiales para el elemento incrustado
   Tamaño CSize = m_rect.Size();
   {
      CClientDC screenDC(NULL);
      screenDC.SetMapMode(MM_HIMETRIC);
      screenDC.DPtoLP(&size);
   }
   INTENTE
   {
      SetExtent(size);  / / puede hacer una espera
   }
   CATCH (CException, e)
   {
      Return FALSE;  / / enlaces no permitirá SetBounds
   }
   END_CATCH
   Devuelve TRUE;
}

\oclient\frame.cpp(50): error C2039: 'InWaitForRelease': no es un miembro de 'COleClientItem'
\oclient\frame.cpp(50): error C2065: 'InWaitForRelease': identificador no declarado
\oclient\frame.cpp(50): error C2064: término no evaluar a una función

En MFC/OLE1 sincrónicas llamadas a la API de un contenedor a un servidor fueron simulados, desde OLE1 era intrínsecamente asincrónica en muchos casos. Era necesario comprobar si hay una llamada asincrónica pendiente en curso antes de procesar los comandos del usuario. MFC/OLE1 proporciona la función COleClientItem::InWaitForRelease para hacerlo. En MFC/OLE 2 no es necesario, por lo que se puede para quitar el reemplazo de OnCommand en CMainFrame todos juntos.

En este punto OCLIENT compilará y vincular.

Otros cambios necesarios

Hay pocas cosas que no son de hecho que mantendrán OCLIENT ejecución, sin embargo. Es mejor solucionar estos problemas ahora en lugar de más tarde.

En primer lugar es necesario inicializar las bibliotecas OLE. Esto se realiza mediante una llamada a AfxOleInit de InitInstance:

if (!.AfxOleInit())
{
  AfxMessageBox ("Error al inicializar las bibliotecas OLE");
  Return FALSE;
}

También es una buena idea para comprobar funciones virtuales para cambios de la lista de parámetros. Una tal función es COleClientItem, se reemplaza en cada aplicación MFC/OLE. Mirando la ayuda en línea, verá que se ha agregado un extra 'DWORD dwParam'. El nuevo CRectItem::OnChange se ve como sigue

void CRectItem::OnChange (OLE_NOTIFICATION wNotification, dwParam DWORD)
{
  Si (m_bTrackServerSize amp; &
        !UpdateItemRectFromServer())
  {
    / / En blanco objeto
    Si (wNotification == OLE_CLOSED)
    {
      / / no destruirlo datos recibidos para el objeto -
      AFIRMAR (!.IsVisible());
      GetDocument() - > DeleteItem(this);
      retorno;   / / ninguna actualización (elemento desaparece ahora)
    }
  }
  Si (wNotification! = OLE_CLOSED)
      Dirty();
  Invalidate();  / / cualquier cambio provocará un repintado
}

En MFC/OLE1 aplicaciones contenedoras derivan la clase de documento COleClientDoc. En MFC/OLE 2 esta clase ha sido eliminada y sustituido por COleDocument (esta nueva organización resulta más fácil construir aplicaciones contenedor/servidor). Hay un # define que asigna COleClientDoc COleDocument para simplificar la portabilidad de las aplicaciones MFC/OLE1 MFC/OLE 2, como por ejemplo OCLIENT. Una de las características no suministradas por COleDocument proporcionado por COleClientDoc es las entradas de mapa de mensajes de comandos estándar. Esto se hace para que las aplicaciones de servidor, que también utilizan COleDocument (indirectamente), no llevar consigo la sobrecarga de estos controladores de comando a menos que sean una aplicación contenedor/servidor. Por lo tanto, necesita agregar las siguientes entradas en el mapa de mensajes de CMainDoc:

ON_UPDATE_COMMAND_UI (ID_EDIT_PASTE, OnUpdatePasteMenu)
ON_UPDATE_COMMAND_UI (ID_EDIT_PASTE_LINK, OnUpdatePasteLinkMenu)
ON_UPDATE_COMMAND_UI (ID_OLE_EDIT_LINKS, OnUpdateEditLinksMenu)
ON_COMMAND (ID_OLE_EDIT_LINKS, COleDocument::OnEditLinks)
ON_UPDATE_COMMAND_UI (ID_OLE_VERB_FIRST, OnUpdateObjectVerbMenu)
ON_UPDATE_COMMAND_UI (ID_OLE_EDIT_CONVERT, OnUpdateObjectVerbMenu)
ON_COMMAND (ID_OLE_EDIT_CONVERT, OnEditConvert)

La aplicación de todos estos comandos es en COleDocument, que es la clase base para el documento.

En este punto, OCLIENT es una aplicación funcional de contenedor OLE. Es posible insertar elementos de cualquier tipo (OLE1 o OLE 2). Ya no se aplica el código necesario para habilitar la activación en el lugar, se editan artículos en una ventana independiente tanto como con OLE1. La siguiente sección describe los cambios necesarios para habilitar la edición en contexto (a veces llamado "Edición Visual").

Agregar "Edición Visual"

Una de las características más interesantes de OLE es activación in situ (o "Edición Visual"). Esta característica permite a la aplicación de servidor asumir porciones de interfaz de usuario del contenedor proporciona una interfaz de edición más transparente para el usuario. Para implementar la activación en lugar de OCLIENT, algunos recursos especiales deban ser añadido, así como algunos código adicional. Estos recursos y el código normalmente son proporcionados por AppWizard — de hecho, gran parte del código aquí fue tomado directamente desde una aplicación de AppWizard fresca con el apoyo de "Contenedor".

En primer lugar, es necesario agregar un recurso de menú que se utilizan cuando hay un elemento que es activo en el lugar. Puede crear este recurso extra del menú de Visual C++ copiando el recurso IDR_OCLITYPE y quitar todo pero los archivo y ventanas emergentes. Dos barras separadoras se insertan entre los archivo y ventanas emergentes para indicar la separación de grupos (debe verse como: archivo || Ventana). Para obtener más información sobre lo que significan estos separadores y cómo se fusionan los menús de servidor y contenedor ver "Menús y recursos: combinación de menús" en Clases OLE 2.

Una vez que tenga estos menús creados, necesita saber el marco sobre ellos. Esto se realiza mediante una llamada a CDocTemplate::SetContainerInfo para la plantilla de documento antes de agregarla a la lista de plantilla de documento en su InitInstance. El nuevo código para registrar la plantilla de documento este aspecto

 CDocTemplate * pTemplate = new CMultiDocTemplate ()
 nbsp;  IDR_OLECLITYPE,
    RUNTIME_CLASS(CMainDoc),
    RUNTIME_CLASS(CMDIChildWnd), / / estándar marco de niño MDI
    RUNTIME_CLASS(CMainView));
pTemplate - > SetContainerInfo(IDR_OLECLITYPE_INPLACE);
AddDocTemplate(pTemplate)

El recurso IDR_OLECLITYPE_INPLACE es el recurso en lugar especial creado en Visual C++.

A fin de habilitar la activación en el lugar, hay algunas cosas que se deben cambiar en el CView (CMainView) derivado de clase como la clase de COleClientItem derivados (CRectItem). Todos estos reemplazos son proporcionados por AppWizard y la mayor parte de la aplicación llegará directamente desde una aplicación de AppWizard predeterminado.

En el primer paso de este puerto, en lugar activación fue deshabilitada completamente reemplazar COleClientItem::CanActivate. Este reemplazo debe eliminarse para permitir la activación en el lugar. Además, NULL pasó a todas las llamadas a DoVerb (hay dos de ellos) porque proporciona que la vista sólo era necesaria para la activación en el lugar. Para aplicar plenamente la activación en el lugar, es necesario pasar el punto de vista correcto en la llamada DoVerb . Una de estas llamadas se encuentra en CMainView::OnInsertObject

pItem-> DoVerb(OLEIVERB_SHOW, this)

Otro está en CMainView::OnLButtonDblClk

m_pSelection-> DoVerb(OLEIVERB_PRIMARY, this)

Es necesario reemplazar OnGetItemPosition. Esto indica al servidor donde poner su ventana relativa a la ventana del contenedor cuando el elemento está activado en el lugar. Para OCLIENT, la aplicación es trivial

void CRectItem::OnGetItemPosition (CRect& rPosition)
{
    rPosition = m_rect;
}

Mayoría de los servidores también implementa lo que se llama "en el lugar el cambio de tamaño." Esto permite que la ventana de servidor ser tamaño y se muevan mientras el usuario está editando el elemento. El contenedor debe participar en esta acción, ya que mover o redimensionar la ventana normalmente afecta a la posición y tamaño dentro del propio documento contenedor. La aplicación de OCLIENT sincroniza el rectángulo interior mantenido por m_rect con la nueva posición y tamaño.

 BOOL CRectItem::OnChangeItemPosition(const CRectamp; rectPos)
{
    ASSERT_VALID(this);

if (!.COleClientItem::OnChangeItemPosition(rectPos))
        Return FALSE;

Invalidate();
    m_rect = rectPos;
    Invalidate();
    GetDocument() - > SetModifiedFlag();

Devuelve TRUE;
}

En este momento, hay suficiente código para permitir que un elemento para ser activado en el lugar y tratar de cambiar el tamaño y mover el elemento cuando está activo, pero no hay código que permitirá al usuario salir de la sesión de edición. Aunque algunos servidores proporcionará esta funcionalidad propios controlando la tecla de escape, se sugiere que los contenedores ofrecen dos formas de desactivar un elemento: (1) pulsando fuera del elemento y (2) pulsando la tecla ESC.

Para la fuga clave añadir un acelerador con Visual C++ que se asigna la clave VK_ESCAPE a un comando, ID_CANCEL_EDIT se añade a los recursos. Sigue el controlador para este comando:

Y el siguiente controlador de comandos proporciona el estándar
/ / interfaz de usuario para cancelar en un lugar de teclado
/ edición session.void CMainView::OnCancelEdit()
{
 nbsp;  / / Cerrar cualquier elemento activo en el lugar en esta vista.
    COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
    Si (pActiveItem! = NULL)
        pActiveItem - > Close();
    Assert(GetDocument()-> GetInPlaceActiveItem(this) == NULL);
}

Para manejar el caso cuando el usuario hace clic fuera del elemento, se agregue el siguiente código al inicio de CMainView::SetSelection

si (pNewSel! = m_pSelection || pNewSel == NULL)
{
 nbsp;  COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
    Si (pActiveItem! = NULL & & pActiveItem! = pNewSel)
        pActiveItem - > Close();
}
  & nbsp
 

Cuando un elemento está activo en el lugar, debe tener el foco. Para asegurarse de que este es el caso manejar OnSetFocus para que enfoque siempre es trasladado al elemento activo cuando su vista recibe el enfoque:

Y tratamiento especial de OnSetFocus y OnSize son necesarios / / cuando un objeto está siendo editado en el lugar.
void CMainView::OnSetFocus (CWnd * pOldWnd)
{
 nbsp;  COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
    Si (pActiveItem! = NULL & &
    pActiveItem - > GetItemState() == COleClientItem::activeUIState)
    {
        / / necesita establecer el foco en este elemento si es la misma vista
        CWnd * Night = pActiveItem - > GetInPlaceWindow();
        Si (Night! = NULL)
        {
            Night - > SetFocus();  / / no llamar a la clase base
            retorno;
        }
    }

CView::OnSetFocus(pOldWnd);
}

Cuando se cambia el tamaño de la vista, es necesario notificar el elemento activo que ha cambiado el rectángulo de recorte. Para ello, que proporcione un controlador para OnSize:

void CMainView::OnSize (UINT nType, int cx, int cy)
{
 nbsp;  CView::OnSize (nType, cx, cy);
    COleClientItem * pActiveItem = GetDocument() - > GetInPlaceActiveItem(this);
    Si (pActiveItem! = NULL)
        pActiveItem - > SetItemRects();
}

Estudio de caso: HIERSVR de MFC 2.0

HIERSVR también fue incluido en MFC 2.0 y aplicación OLE con MFC/OLE1. Esta nota describe brevemente los pasos que esta aplicación se convirtió inicialmente utilizar las clases MFC/OLE 2. Una serie de características se añadieron después de que el puerto inicial se completó para ilustrar mejor las clases MFC/OLE 2. Estas características no se tratarán aquí; se refieren a la muestra en sí mismo para obtener más información sobre las características avanzadas.

&Notanbsp;  Los errores del compilador y el proceso paso a paso fue creado con Visual C++ 2.0. Mensajes de error específicos y ubicaciones pueden haber cambiado con Visual C++ 4.0, pero la información conceptual sigue siendo válida.

Lo marcha

El enfoque adoptado para la muestra HIERSVR MFC/OLE de puerto es empezar por crearla y corregir los errores del compilador obvio que dará como resultado. Si se toma la muestra HIERSVR de MFC 2.0 y compilarlo en esta versión de MFC, encontrará que no hay muchos errores para resolver (aunque hay más con el ejemplo OCLIENT). A continuación se describen los errores en el orden en que ocurren normalmente.

Compilación y corrección de errores

\hiersvr\hiersvr.cpp(83): error C2039: 'RunEmbedded': no es un miembro de 'COleTemplateServer'

Este primer error señala un problema mucho más grande con la función InitInstance para servidores. La inicialización requerida para un servidor OLE es probablemente uno de los mayores cambios que vas a tener que hacer la aplicación MFC/OLE1 para ponerlo en marcha. Lo mejor es mirar qué AppWizard crea un servidor OLE y modifique el código según corresponda. Aquí hay algunos puntos a tener en cuenta:

Es necesario inicializar las bibliotecas OLE llamando AfxOleInit

Llame a SetServerInfo en el objeto de plantilla de documento para establecer controles de recursos de servidor y la información de la clase de tiempo de ejecución que no se puede establecer con el constructor de CDocTemplate.

No mostrar la ventana principal de la aplicación si /Embedding está presente en la línea de comandos.

Necesitará un GUID para el documento. Se trata de un identificador único para el tipo de documento (128 bits). AppWizard creará uno para usted, así que si utilizas la técnica descrita aquí de copiar el nuevo código de una nueva aplicación de servidor de AppWizard generado, usted puede simplemente "robar" el GUID de esa aplicación. Si no es así, puede utilizar el GUIDGEN.Utilidad EXE en el directorio BIN.

Es necesario para "conectar" su objeto COleTemplateServer a la plantilla de documento llamando al COleTemplateServer::ConnectTemplate.

Actualizar el registro del sistema cuando se ejecuta su aplicación independiente. Así, si el usuario mueve el.EXE para su aplicación, ejecución desde su nueva ubicación se actualizará la base de datos de Windows sistema registro para que apunte a la nueva ubicación.

Después de aplicar todos estos cambios basados en lo que crea un AppWizard para InitInstance, InitInstance (y GUID relacionado) de HIERSVR deben decir lo siguiente:

// this is the GUID for HIERSVR documents
static const GUID BASED_CODE clsid =
    { 0xA0A16360L, 0xC19B, 0x101A, { 0x8C, 0xE5, 0x00, 0xDD, 0x01, 0x11, 0x3F, 0x12 } };
    
/////////////////////////////////////////////////////////////////////////////
// COLEServerApp initialization

BOOL COLEServerApp::InitInstance()
{
    // OLE 2 initialization
    if (!AfxOleInit())
    {
        AfxMessageBox("Initialization of the OLE failed!");
        return FALSE;
    }

    // Standard initialization
    LoadStdProfileSettings(); // Load standard INI file options 

    // Register document templates
    CDocTemplate* pDocTemplate;
    pDocTemplate = new CMultiDocTemplate(IDR_HIERSVRTYPE,
        RUNTIME_CLASS(CServerDoc),   
        RUNTIME_CLASS(CMDIChildWnd),
        RUNTIME_CLASS(CServerView));
    pDocTemplate->SetServerInfo(IDR_HIERSVRTYPE_SRVR_EMB);
    AddDocTemplate(pDocTemplate);

    // create main MDI Frame window
    CMainFrame* pMainFrame = new CMainFrame;
    if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
        return FALSE;
    m_pMainWnd = pMainFrame;

    SetDialogBkColor();   // gray look

    // enable file manager drag/drop and DDE Execute open
    m_pMainWnd->DragAcceptFiles();
    EnableShellOpen();
    
    m_server.ConnectTemplate(clsid, pDocTemplate, FALSE);
    COleTemplateServer::RegisterAll();

    // try to launch as an OLE server
    if (RunEmbedded())
    {
        // "short-circuit" initialization -- run as server!
        return TRUE;
    }
    m_server.UpdateRegistry();
    RegisterShellFileTypes();

    // not run as OLE server, so show the main window
    if (m_lpCmdLine[0] == '\0')
    {
        // create a new (empty) document
        OnFileNew();
    }
    else
    {
        // open an existing document
        OpenDocumentFile(m_lpCmdLine);
    }

    pMainFrame->ShowWindow(m_nCmdShow);
    pMainFrame->UpdateWindow();
    
    return TRUE;
}

Observará que el código anterior se refiere a un nuevo ID de recurso, IDR_HIERSVRTYPE_SRVR_EMB. Este es el recurso de menú para utilizarse cuando se edita un documento que está incrustado en otro contenedor. En MFC/OLE1 fueron modificados los elementos de menú específicos para editar un elemento incrustado sobre la marcha. Utilizando una estructura de menú completamente diferente al editar un elemento incrustado en lugar de un documento basado en el archivo de edición facilita mucho proporcionar diferentes interfaces de usuario para estos dos modos separados. Como verá más adelante, se utiliza un recurso de menú totalmente independiente al editar un objeto incrustado en el lugar.

Para crear este recurso, cargar el script del recurso en Visual C++ y copiar los recursos de menú IDR_HIERSVRTYPE existentes. Cambie el nombre del nuevo recurso IDR_HIERSVRTYPE_SRVR_EMB (esta es la misma Convención de nomenclatura que utiliza el Asistente para aplicaciones). A continuación cambie "Guardar archivo" a "Actualización de archivo"; asígnele el identificador de comando ID_FILE_UPDATE. También cambiar "Archivo Guardar como" para "Archivo Guardar copia como"; asígnele el identificador de comando ID_FILE_SAVE_COPY_AS. El marco proporciona la implementación de ambos de estos comandos.

\hiersvr\svritem.h(60): error C2433: 'OLESTATUS': 'virtual' no permitido en las declaraciones de datos
\hiersvr\svritem.h(60): error C2501: 'OLESTATUS': faltan los especificadores de /Decl
\hiersvr\svritem.h(60): error C2146: error de sintaxis: falta el ';' antes de identificador 'OnSetData'
\hiersvr\svritem.h(60): error C2061: error de sintaxis: identificador 'OLECLIPFORMAT'
\hiersvr\svritem.h(60): error C2501: 'OnSetData': faltan los especificadores de /Decl

Hay una serie de errores resultantes de la anulación de OnSetData, ya que se refiere al tipo de OLESTATUS . OLESTATUS era la forma que OLE1 devuelve errores. Esto ha cambiado a HRESULT en OLE 2, aunque MFC normalmente convierte un valor HRESULT en un COleException que contiene el error. En este caso, el reemplazo de OnSetData ya no es necesario, por lo que lo más fácil es quitarlo.

 \hiersvr\svritem.cpp(30): error C2660: 'COleServerItem::COleServerItem': función no toman parámetros 1

El constructor de COleServerItem toma un parámetro adicional de 'BOOL'. Este indicador determina cómo se realiza la administración de la memoria sobre los objetos de COleServerItem . Por si se establece en TRUE, el marco controla la administración de la memoria de estos objetos: eliminarlos cuando ya no sean necesarias. HIERSVR utiliza objetos CServerItem (derivado de COleServerItem) como parte de sus datos nativos, por lo que podrá establecer este indicador en FALSE. Esto permite HIERSVR determinar cuándo se elimina cada elemento del servidor.

\hiersvr\svritem.cpp(44): error C2259: 'CServerItem': intento ilegal de crear una instancia de clase abstracta
\hiersvr\svritem.cpp(44): error C2259: 'CServerItem': intento ilegal de crear una instancia de clase abstracta

Como estos errores implican, hay algunas funciones 'puros virtuales' que no haya anulado en CServerItem. Más probable es que esto es causado por el hecho de que ha cambiado la lista de parámetros de OnDraw. Para corregir este error, cambiar CServerItem::OnDraw como sigue (así como la declaración de svritem.h)

BOOL CServerItem::OnDraw(CDC* pDC, CSizeamp; rSize)
{
    / / solicitud de OLE para dibujar el nodo
    pDC - > SetMapMode(MM_TEXT); y siempre en píxeles
    volver a DoDraw (pDC, CPoint(0,0), FALSE);
}

El nuevo parámetro es 'rSize'. Esto le permite rellenar el tamaño del plano, si es conveniente. Este tamaño debe ser en HIMETRIC. En este caso, no es conveniente llenar este valor, por lo que el marco llama a OnGetExtent para recuperar la medida. Para que funcione, vas a tener que implementar OnGetExtent:

BOOL CServerItem::OnGetExtent(DV&ASPECT dwDrawAspect, CSizeamp; rSize)
{
    Si (dwDrawAspect! = DVASPECT_CONTENT)
        volver a COleServerItem::OnGetExtent (dwDrawAspect, rSize);
        
    rSize = CalcNodeSize();
    Devuelve TRUE;
}

\hiersvr\svritem.cpp(104): error C2065: 'm_rectBounds': identificador no declarado
\hiersvr\svritem.cpp(104): error C2228: izquierdo de '.SetRect' debe tener el tipo struct/clase/union
\hiersvr\svritem.cpp(106): error C2664: ' void __pascal DPtoLP __far (struct:: tagPOINT __far *, int) __far const ': no se puede convertir el parámetro 1 de ' int __far *' a ' struct:: tagPOINT __far *'

En el CServerItem::CalcNodeSize función el tamaño del elemento se convierte en HIMETRIC y almacenado en m_rectBounds. El miembro de indocumentados 'm_rectBounds' de COleServerItem no existe (ha sido parcialmente reemplazado por m_sizeExtent, pero en OLE 2 este miembro tiene un uso ligeramente diferente que m_rectBounds en OLE1). En lugar de establecer el tamaño HIMETRIC en esta variable de miembro, podrá devolverlo. Este valor devuelto se utiliza en OnGetExtent, implementado anteriormente.

CSize CServerItem::CalcNodeSize()
{
 nbsp;  CClientDC dcScreen(NULL);

m_sizeNode = dcScreen.GetTextExtent (m_strDescription,
      m_strDescription.GetLength());
    m_sizeNode += CSize (CX_INSET * 2, CY_INSET * 2);

/ / Establecer tamaño HIMETRIC sugerido
    CSize tamaño (m_sizeNode.cx, m_sizeNode.cy);
    dcScreen.SetMapMode(MM_HIMETRIC);
    dcScreen.DPtoLP(&size);
    devolver tamaño;
}

CServerItem también reemplaza COleServerItem::OnGetTextData. Esta función es obsoleta en MFC/OLE y es sustituida por un mecanismo diferente. La versión 3.0 de MFC de la muestra de MFC OLE HIERSVR implementa esta funcionalidad mediante el reemplazo de COleServerItem::OnRenderFileData. Esta funcionalidad no es importante para este puerto básico, por lo que puede quitar el reemplazo de OnGetTextData.

Hay muchos más errores en svritem.cpp que no han sido abordados. No son errores "reales" — sólo errores causados por errores anteriores.

\hiersvr\svrview.cpp(325): error C2660: 'CopyToClipboard': función no tome 2 parámetros

COleServerItem::CopyToClipboard ya no es compatible con la bandera de 'bIncludeNative'. Siempre se copian los datos nativos (los datos escritos por función de Serialize del elemento del servidor), por eso quite el primer parámetro. Además, CopyToClipboard se producirá una excepción cuando se produce un error en lugar de devolver FALSE. Cambie el código para CServerView::OnEditCopy como sigue

void CServerView::OnEditCopy()
{
 nbsp;  Si (m_pSelectedNode == NULL)
        AfxThrowNotSupportedException();
        
    INTENTE
    {
        m_pSelectedNode - > CopyToClipboard(TRUE);
    }
    CATCH_ALL(e)
    {
        AfxMessageBox ("copiar al Portapapeles error");
    }
    END_CATCH_ALL}

Aunque hubo más errores resultantes de la compilación de la versión 2.0 de MFC de HIERSVR que hubo para la misma versión de OCLIENT, fueron en realidad menos cambios.

En este punto HIERSVR compilará y vincular y funcionar como un servidor OLE, pero sin la función de edición en el lugar, se ejecutará a continuación.

Agregar "Edición Visual"

Para agregar "Edición Visual" (o en el lugar activación) a esta aplicación de servidor, hay sólo un par de cosas de que se debe tener cuidado:

El recurso de menú es fácil de crear. Ejecutar Visual C++, con copia a la IDR_HIERSVRTYPE de recursos de menú a un recurso de menú llamado IDR_HIERSVRTYPE_SRVR_IP. Modificar el menú para que queden sólo las emergentes menú Editar y ayuda. Agregación dos separadores al menú entre los menús Edit y ayuda (debe ser similar: editar || Ayuda). Para obtener más información sobre lo que significan estos separadores y cómo se fusionan los menús de servidor y contenedor, consulte "Menús y recursos: combinación de menús" en Clases OLE 2.

El mapa de bits de la barra de herramientas del subconjunto puede crearse fácilmente mediante la copia de una nueva solicitud de AppWizard generado con una opción de "Servidor" marcada. Este mapa de bits puede importarse en Visual C++. Asegúrese de dar el mapa de bits de un ID de IDR_HIERSVRTYPE_SRVR_IP.

La clase derivada de COleIPFrameWnd puede copiarse desde una aplicación de AppWizard generado con soporte de servidor así. Copiar ambos archivos, IPFRAME.CPP y IPFRAME.H y agregarlos al proyecto. Asegúrese de que la llamada LoadBitmap se refiere a IDR_HIERSVRTYPE_SRVR_IP, el mapa de bits creado en el paso anterior.

Ahora que se crean todos los nuevos recursos y clases, agregar el código necesario para que el marco sabe acerca de estos (y sabe que esta aplicación ahora soporta en lugar de edición). Esto se realiza agregando que algunos parámetros más que los SetServerInfo llaman en la función InitInstance:

pDocTemplate->SetServerInfo (IDR_HIERSVRTYPE_SRVR_EMB,
    IDR_HIERSVRTYPE_SRVR_IP, RUNTIME_CLASS(CInPlaceFrame))

Ahora está listo para ejecutarse en el lugar en cualquier contenedor que también apoya la activación en el lugar. Pero, es un error menor sigue al acecho en el código. HIERSVR es compatible con un menú contextual que aparece cuando el usuario presiona el botón derecho del ratón. Este menú funciona cuando HIERSVR es totalmente abierto, pero no funciona cuando se edita un incrustación en el lugar. La razón puede ser inmovilizada a esta línea de código en CServerView::OnRButtonDown

pMenu-gt;TrackPopupMenu(TPM_CENTERALIGN | TPM_RIGHTBUTTON,
    Point.x, point.y, AfxGetApp() - > m_pMainWnd)

Observe la referencia a () AfxGetApp-gt; m_pMainWnd. Cuando el servidor está activado en el lugar, tiene una ventana principal y m_pMainWnd está definido, pero es normalmente invisible. Además, esta ventana se refiere a la ventana principal de la aplicación, la ventana de marco MDI que aparece cuando el servidor está completamente abierto o ejecuta independiente. No hace referencia a la ventana de marco activo — que cuando en el lugar activado es una ventana de marco derivada de COleIPFrameWnd. Para obtener la ventana activa correcta incluso cuando en el lugar de edición, esta versión de MFC agrega una nueva función, AfxGetMainWnd. Generalmente, debería utilizar esta función en lugar de AfxGetApp() - > m_pMainWnd. Este código necesita cambiar lo siguiente:

 pMenu-gt;TrackPopupMenu(TPM_CENTERALI&GN | TPM_RIGHTBUTTON,
    Point.x, point.y, AfxGetMainWnd())

Ahora dispone de un servidor OLE mínimamente habilitado para activación funcional en el lugar. Pero todavía hay muchas características disponibles con 2 MFC/OLE que no estaban disponibles en MFC/OLE1. Consulte el ejemplo HIERSVR para más ideas sobre las características que desee aplicar. Algunas de las características que implementa HIERSVR se enumeran a continuación:

El ejemplo HIERSVR en MFC 3.0 también utiliza un diseño ligeramente diferente para los elementos de sus servidor. Esto ayuda a conservar la memoria y hace más flexibles sus vínculos. Con la versión 2.0 de HIERSVR cada nodo en el árbol es un COleServerItem. COleServerItem lleva un poco más carga de lo estrictamente necesario para cada uno de estos nodos, pero una COleServerItem es necesario para cada vínculo activo. Pero en su mayor parte, hay muy pocos vínculos activos en cualquier momento dado. Para hacerlo más eficiente, el HIERSVR en esta versión de MFC separa el nodo de la COleServerItem. Tiene una CServerNode y una clase CServerItem . La CServerItem (derivado de COleServerItem) sólo se crea cuando sea necesario. Una vez que el contenedor (o contenedores) dejar de usar ese vínculo particular a ese nodo en particular, se elimina el objeto CServerItem asociado con el CServerNode. Este diseño es más eficiente y más flexible. Su flexibilidad es cuando ocupan varios vínculos de selección. Ninguna de estas dos versiones de HIERSVR apoyo de selección múltiple, pero sería mucho más fácil añadir (y admite enlaces a esas selecciones) con la versión 3.0 de MFC de HIERSVR, ya que el COleServerItem está separada de los datos nativos.

&Notas técnicas por número |nbsp; Notas técnicas por categoría

Index