Ver Mensaje Individual
  #2 (permalink)  
Antiguo 24/02/2015, 04:50
eferion
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 7 meses
Puntos: 204
Respuesta: Problemas con IPictureDisp en C++Builder

Código C++:
Ver original
  1. IPictureDisp** ExportPicture(ItemSetConstants ItemSet,  short IncludeBackColor, short IncludeBackPick);

Si ExportPicture devuelve un IPictureDisp**, es decir, un puntero doble, no puedes coger ese valor de retorno y convertirlo por arte de magia en un puntero simple:

Código C++:
Ver original
  1. pIPD->operator =((IPictureDisp*)AddFlow1->ExportPicture((afSelectedItems, true, true));

De hecho, si quitas ese cast "(IPictureDisp*)" que tienes, verás que el código da error... y es por algo.

Si ExportPicture devuelve un puntero doble puede ser porque este método puede devolver una lista de elementos... si tu únicamente necesitas uno, puedes optar por coger el primero:

Código C++:
Ver original
  1. pIPD->operator = *(AddFlow1->ExportPicture(afSelectedItems, true, true);

Lo que pasa es que así estás perdiendo la referencia al puntero doble y, en consecuencia, vas a crear lagunas de memoria.

Lo suyo sería que almacenases en algún sitio el puntero doble y después hicieses la asignación:

Código C++:
Ver original
  1. IPictureDisp** lista = AddFlow1->ExportPicture(afSelectedItems, true, true);
  2. pIPD->operator = *lista /* o lista[ 0 ] */;

En otro orden de cosas:

Código C++:
Ver original
  1. try
  2. {
  3.   pic = new TPicture; //Solicito memoria para el TPicture
  4.    __try
  5.   {
  6.     try
  7.     {
  8.       // ...
  9.     }
  10.     catch(...)
  11.     {
  12.       // ...
  13.     }
  14.   }
  15.   __finally
  16.   {
  17.   }
  18. }
  19. catch(...)
  20. {
  21.   // ...
  22. }

Esta estructura de try-catch es un poco compleja:

El primer try-catch se supone que está para controlar el "new"... en ese caso, capturar todas las excepciones que se produzcan y mostrar siempre el mismo mensaje de error no parece una buena idea. Hay dos opciones:

* Encerrar únicamente el new dentro del try-catch y capturar la excepción concreta:

Código C++:
Ver original
  1. try
  2. {
  3.   pic = new TPicture;
  4. }
  5.  
  6. catch ( std::bad_alloc& )
  7. {
  8.   ShowMessage("No hay memoria suficiente.");
  9. }

Eso sí, ten en cuenta que si no tienes memoria para crear un TPicture, posiblemente tampoco tengas memoria para mostrar el mensaje de error.

Un truco para esto suele ser reservar 1k de memoria (por ejemplo). La idea es liberarlo justo antes de mostrar el mensaje de error para que el sistema encuentre memoria libre. Pero vamos, es un caso crítico que no se suele dar y que si se da vas a perder información fijo.

* Forzar a que el new no pueda dar una excepción

Código C++:
Ver original
  1. pic = new (std::nothrow) TPicture;
  2. if ( pic == 0 )
  3. {
  4.   // No se ha podido reservar la memoria
  5.   // o se ha producido un fallo en la creación del objeto
  6. }

Este método tiene como ventaja que no provoca excepciones, la desventaja es que si se produce un fallo durante la creación del objeto no lo puedes detectar.

Más cosillas. El bloque _try-__finally: este bloque no tiene sentido básicamente porque dentro tiene otro bloque que ya se encarga de capturar todas las excepciones... y dado que el catch de este segundo bloque no lanza excepciones nuevas, SIEMPRE se va a ejecutar lo que hay en __finally... exista el __try-__finally o no.

Si te quieres quedar más tranquilo, puedes encapsular el objeto en un smart pointer (std::auto_ptr en C++, std::unique_ptr en C++11 o boost::unique_ptr si prefieres usar la librería boost). La ventaja de los smart pointers es que se encargan de liberar la memoria del objeto, por lo que puedes despreocuparte de la liberación de memoria... eso sí, hay que usarlos con cierto criterio:

Código C++:
Ver original
  1. std::unique_ptr< TPicture > smart_pic{ new TPicture };
  2. try
  3. {
  4.   SetOlePicture(smart_pic.get( ), *pIPD); //Paso la imagen del IPictureDisp al TPicture
  5.  
  6.   //Envio la imagen al portapapeles
  7.   smart_pic->SaveToClipboardFormat(MyFormat,DataHandle,APalette);
  8.   Clipboard()->SetAsHandle(MyFormat,DataHandle);
  9. }catch(...){
  10.   ShowMessage("Fallo al copiar la imagen al portapapeles.");
  11. }

Claro que, ya puestos, lo más sencillo sería crear el TPicture en el stack y dejarnos de memoria dinámica que tampoco es necesario:

Código C++:
Ver original
  1. TPicture stack_pic;
  2. try
  3. {
  4.   SetOlePicture(&stack_pic, *pIPD); //Paso la imagen del IPictureDisp al TPicture
  5.  
  6.   //Envio la imagen al portapapeles
  7.   stack_pic.SaveToClipboardFormat(MyFormat,DataHandle,APalette);
  8.   Clipboard()->SetAsHandle(MyFormat,DataHandle);
  9. }catch(...){
  10.   ShowMessage("Fallo al copiar la imagen al portapapeles.");
  11. }

Y para rematar... el último try-catch también captura una excepción genérica... no es buena idea este tipo de prácticas porque a tí tampoco te va a permitr identificar el error que se ha producido... dentro de un catch(...) no tienes información sobre la excepción que se ha producido.

Además, tu piensa que al usuario final le tienes que hacer llegar una versión ya depurada, por lo que no debería producirse bajo ningúna circunstancia y salvo casos altamente excepcionales una excepción genérica.

Un saludo.