Foros del Web » Programación para mayores de 30 ;) » C/C++ »

Problemas con IPictureDisp en C++Builder

Estas en el tema de Problemas con IPictureDisp en C++Builder en el foro de C/C++ en Foros del Web. Hola amigos, estoy usando un componente ActiveX llamado AddFlow, el cual permite dibujar diagramas. El componente se puede usar de manera libre y te muestra ...
  #1 (permalink)  
Antiguo 24/02/2015, 03:39
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Problemas con IPictureDisp en C++Builder

Hola amigos, estoy usando un componente ActiveX llamado AddFlow, el cual permite dibujar diagramas. El componente se puede usar de manera libre y te muestra un mensaje diciendo que ha sido creado con la version no registrada y claro, antes de comprar nada lo tengo que probar y mas esto que cuesta la licencia unos 700€. La empresa que creó el AddFlow no me da soporte porque dice que solo dan soporte a las personas que tienen licencia, algo que no comparto ya que primero hay que probarlo bien y si te convence lo compras y si no te convence pues no lo compras.
Os explico el problema.
El componente tiene el método:
Código PHP:
IPictureDisp** ExportPicture(ItemSetConstants ItemSetshort IncludeBackColorshort IncludeBackPick); 
En la ayuda del componente podemos ver solo ejemplos de Visual Basic:
Código:
Example 1 (VB)

' Export the diagram to a picture box:
' here, we copy only the selected items.
Picture1.Picture = AddFlow1.ExportPicture(afSelectedItems, False, False)
Example 2 (VB)

' Copy the diagram to the clipboard:
' here we wish to copy all items and the back picture.
Clipboard.Clear
Clipboard.SetData AddFlow1.ExportPicture(afAllItems, False, True)
Example 3 (VB)

' Save the diagram in a file:
' here we don›t want to save the grid.
Dim pic As IpictureDisp
AddFlow1.ShowGrid = False
Set pic = AddFlow1.ExportPicture(afAllItems, True, True) 
AddFlow1.ShowGrid = True
SavePicture pic, "myfile.emf"
Haciendo pruebas en C++Builder he llegado a esto:
Código PHP:
unsigned int DataHandle;
   
HPALETTE APalette;
   
unsigned short MyFormat;
   
TPicture *pic;
   
_di_IPictureDisp *pIPD = new _di_IPictureDisp;

   
//ExportPicture devuelve un IPictureDisp** y quiero pasarlo a un _di_IPictureDisp
   
pIPD->operator =((IPictureDisp*)AddFlow1->ExportPicture((afSelectedItemstruetrue));

   try
   {
      
pic = new TPicture//Solicito memoria para el TPicture
      
__try
      
{
         try
         {
            
SetOlePicture(pic, *pIPD); //Paso la imagen del IPictureDisp al TPicture

            //Envio la imagen al portapapeles
            
pic->SaveToClipboardFormat(MyFormat,DataHandle,APalette);
            
Clipboard()->SetAsHandle(MyFormat,DataHandle);
         }catch(...){
            
ShowMessage("Fallo al copiar la imagen al portapapeles.");
         }
      }
__finally{
         
delete pic//Libero la memoria del TPicture
      
}
   }catch(...){
      
ShowMessage("No hay memoria suficiente.");
   } 
El caso es que no funciona y no se como conseguir hacerlo. ¿Alguien puede ayudarme?
  #2 (permalink)  
Antiguo 24/02/2015, 04:50
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 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.
  #3 (permalink)  
Antiguo 25/02/2015, 04:27
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Problemas con IPictureDisp en C++Builder

Gracias amigo, como no tenia muy claro muchas cosas, he comentado la parte donde obtengo el **IPictureDisp y me he centrado por ahora solo en la parte de copiar el _di_IPictureDisp al TPicture y el caso es que ahi ya da fallo al intentar copiar la imagen al portapapeles. El codigo ahora mismo lo tengo así despues de las modificaciones:
Código PHP:
void __fastcall TFormMain::Imagendeseleccionadosalportapapeles1Click(
      
TObject *Sender)
{
   
unsigned int DataHandle=0;
   
HPALETTE APalette=0;
   
unsigned short MyFormat=0;
   
TPicture *pic, *pic2;
   
IPictureDisp** lista;
   
_di_IPictureDisp *pIPD;
   
bool error false;

   
pIPD = new (std::nothrow)_di_IPictureDisp;
   if(
pIPD == 0){
      
error true;
   }else{
      
pic = new (std::nothrow)TPicture//Solicito memoria para el TPicture
      
if(pic == 0){
         
error true;
         
delete pIPD;
      }else{
         
pic2 = new (std::nothrow)TPicture//Solicito memoria para el TPicture
         
if(pic2 == 0){
            
error true;
            
delete pIPD;
            
delete pic;
         }
      }
   }

   if(
error == true){
      
ShowMessage("No hay memoria suficiente para realizar esta acción.");
   }else{
      try
      {
         
//ExportPicture devuelve un IPictureDisp** y quiero pasarlo a un _di_IPictureDisp
         //lista = AddFlow1->ExportPicture(afSelectedItems, true, true);
         //pIPD->operator = (lista[0]);

         
pic->LoadFromFile("prueba.bmp");

         
GetOlePicture(pic, *pIPD);
         
SetOlePicture(pic2, *pIPD); //Paso la imagen del IPictureDisp al TPicture

         //Envio la imagen al portapapeles
         
pic2->SaveToClipboardFormat(MyFormat,DataHandle,APalette);
         
Clipboard()->SetAsHandle(MyFormat,DataHandle);
      }catch(...){
         
ShowMessage("Fallo al copiar la imagen al portapapeles.");
      }
      
delete pic//Libero la memoria del TPicture
      
delete pic2;
      
delete pIPD;
   }

Cuando intenta ejecutar la linea:
Código PHP:
pic2->SaveToClipboardFormat(MyFormat,DataHandle,APalette); 
me da la excepcion:
Cita:
Project Project1.exe raised exception class EInvalidGraphicOperation with message 'Invalid operation on TOleGraphic'. Process stopped.
Y claro, si no me funciona ni la parte que copia la imagen de un _di_IPictureDisp a un TPicture, pues creo que primero tengo que hacer que este ejemplo funcione y luego enfocarme en la otra parte donde tengo que obtener el IPictureDisp.
La verdad es que no tengo ni idea de porque falla. ¿pueden ayudarme?
  #4 (permalink)  
Antiguo 25/02/2015, 05:19
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Problemas con IPictureDisp en C++Builder

Navegando por internet he visto que hay algunos temas en los que tratan este problema. No se si será debido a un fallo en la librería de borland o a una mala configuración del objeto _di_IPictureDisp, pero después de hacer:

Código C++:
Ver original
  1. GetOlePicture(pic, *pIPD);
  2. SetOlePicture(pic2, *pIPD);

El resultado en pic2 es una imagen corrupta... seguramente tus problemas vengan de ese punto. Prueba a guardar pic2 después del SetOlePicture y comprueba si la imagen se ve correctamente o no.

PD.: si es un bug viene de antiguo, he encontrado un hilo tratando este mismo problema... en el 2005!!!

Un saludo.
  #5 (permalink)  
Antiguo 26/02/2015, 01:32
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Problemas con IPictureDisp en C++Builder

Pues tienes razón amigo, al parecer es un bug ya que la imagen que obtengo es corrupta.
Buscando por internet he encontrado un enlace donde explican como repararlo: http://www.griaulebiometrics.com/en-us/manual/afis-sdk/troubleshooting/delphi-getolepicture-bug-fix

He seguido esas instrucciones pero al llegar a esta línea:
Cita:
Cita:
Move the compiled file (AXCtrls.dcu) to your project directory and put AXCtrls as the first unit in your projects uses-clause.
Lo que hago es mover el .dcu que se crea al directorio del proyecto pero la otra parte de la frase no la entiendo y no se que tengo que hacer. ¿Alguien puede ayudarme?
  #6 (permalink)  
Antiguo 26/02/2015, 02:05
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Problemas con IPictureDisp en C++Builder

A ver, la solución te la están dando para Delphi, que está basado en pascal. En Delphi hay un elemento equivalente al include y es "uses". Lo que viene a decir ese mensaje es que el include de la solución lo debes poner en primer lugar.

En el caso de C++ la solución debería ser diferente. Básicamente porque en delphi no hay archivos de cabecera y en C++ sí, luego tu en C++ no puedes "incluir" un cpp sino que los includes suelen afectar a archivos de cabecera.

Imagino que la solución pasará por mover la librería a tu proyecto y poner el include a la cabecera de este archivo en primer lugar... pero no te puedo garantizar que funcione.

Un saludo
  #7 (permalink)  
Antiguo 26/02/2015, 02:38
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Problemas con IPictureDisp en C++Builder

El problema que tengo es que en los directorios del C++Builder están: axctrls.pas y axctrls.hpp pero no aparece ningun axctrls.cpp por ningun sitio. ¿como lo hago para añadirlo? ¿entonces el axctrls.dcu que he creado no sirve para nada en C++Builder?
  #8 (permalink)  
Antiguo 26/02/2015, 02:56
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Problemas con IPictureDisp en C++Builder

Yo estuve usando C++ builder hace cosa de, no se, 14 años. Lo abandoné por la cantidad de erorres que tenían sus librerías y no lo he vuelto a tocar.

En este caso, Borland tiene su propio compilador, que funciona como a ellos les intersa. Normalmente el código objeto que genera cada compilador no puede ser aprovechado por compiladores de diferentes marcas. Si en tu caso, el compilador genera código objeto con extensión dcu, pues entonces será el equivalente a las librerías .a que genera mingwin o a los .lib que genera el compilador de microsoft.

Tu prueba la solución a ver si te funciona. Lo que tienes que hacer es añadir esa librería a tu proyecto para que se compile todo junto.

Aún así ya te digo que .pas es una extensión más propia de delphi que de c++, pero poco más te puedo decir al respecto.

Un saludo.
  #9 (permalink)  
Antiguo 26/02/2015, 03:01
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Problemas con IPictureDisp en C++Builder

Lo que si he podido comprobar es que entre la imagen original y la resultante hay una diferencia solo, la de salida tiene 8 bytes mas y, al mirarlas con un editor hexadecimal veo esto:
Original: 0x42 0x4D 0x36 0xA3 0x02 0x00 0x00 0x00 0x00 0x00 0x36 0x00 0x00 0x00 0x28 0x00...
Resultado: 0x6C 0x74 0x00 0x00 0x36 0xA3 0x02 0x00 0x42 0x4D 0x36 0xA3 0x02 0x00 0x00 0x00 0x00 0x00 0x36 0x00 0x00 0x00 0x28 0x00...

Se puede observar que los 8 bytes de mas los añade al inicio del nuevo archivo y si elimino esos 8 bytes la imagen ya se ve correctamente.
Hasta ahí he podido llegar.
  #10 (permalink)  
Antiguo 26/02/2015, 03:18
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Problemas con IPictureDisp en C++Builder

También puedes "trampear" tu código para que solucione ese problema y evitas tener que recompilar esa librería... pero en ese caso tendrás que estar pendiente cuando cambies de versión de borland para eliminar esa trampa si resulta que el error ya está corregido.
  #11 (permalink)  
Antiguo 26/02/2015, 13:30
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 2 meses
Puntos: 3
Respuesta: Problemas con IPictureDisp en C++Builder

Cita:
Iniciado por eferion Ver Mensaje
También puedes "trampear" tu código para que solucione ese problema y evitas tener que recompilar esa librería... pero en ese caso tendrás que estar pendiente cuando cambies de versión de borland para eliminar esa trampa si resulta que el error ya está corregido.
Amigo ya he conseguido obtener la imagen desde el componente pero sigo con el problema de los 8 bytes de mas y ni siquiera pasa por la funcion que indican en la funcion que habia que parchear. El problema tiene que estar en otro lugar y no tengo ni idea. Te pongo como queda el codigo para guardar la imagen en un archivo a espensas de poder solucionar lo de los 8 bytes:
Código PHP:
void __fastcall TFormMain::ButtonSaveImageClick(TObject *Sender)
{
   
//ShowMessage("Esta opcion no es funcional por ahora.");

   
TPicture *pic;
   
_di_IPictureDisp pIPD;
   
bool error false;

   try
   {
      
pic = new TPicture//Solicito memoria para el TPicture
   
}catch(std::bad_alloc&){
      
error true;
   }

   if(
error == true){
      
ShowMessage("No hay memoria suficiente para realizar esta acción.");
   }else{
      try
      {
         
//ExportPicture devuelve un IPictureDisp** y quiero pasarlo a un _di_IPictureDisp
         
pIPD = (IPictureDisp*)AddFlow1->ExportPicture(afAllItemstruetrue);

         
SetOlePicture(picpIPD); //Paso la imagen del IPictureDisp al TPicture

         
SaveDialog1->Filter "Enhanced Metafile Format|*.emf";
         if(
SaveDialog1->Execute()){
            
SaveDialog1->FileName ChangeFileExt(SaveDialog1->FileName".emf");
            
pic->SaveToFile(SaveDialog1->FileName);
         }
      }catch(...){
         
ShowMessage("Fallo al copiar la imagen al portapapeles.");
      }
      
delete pic//Libero la memoria del TPicture
   
}


Etiquetas: funcion, int
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 20:06.