Pb de ClipBoard

cs_totodude Messages postés 24 Date d'inscription vendredi 19 mars 2004 Statut Membre Dernière intervention 6 avril 2004 - 19 mars 2004 à 18:31
chichif Messages postés 2 Date d'inscription mercredi 20 avril 2005 Statut Membre Dernière intervention 25 avril 2005 - 25 avril 2005 à 10:47
Bonjour tout le monde,

je voudrais réaliser une application qui réalise une impression écran et découpe un tableau sur l'image ainsi enregistrée. j'ai déjà jeté un oeil sur les questions du forum en rapport mais je n'y ai pas trouvé mon bonheur.

voici donc ou j'en suis :
1) je réalise mon impression écran

keybd_event(VK_SNAPSHOT,1,0,0);

2) j'ouvre mon clipboard

::OpenClipboard(NULL);

3) je récupère ce qu'il y a dedans
HBITMAP* hClip = (HBITMAP*)GetClipboardData(CF_BITMAP);

4) je découpe l'image
CImage cImage;
cImage.Attach(*hClip);
HDC hImage=NULL;
cImage.Draw(hImage, 100, 100, 100, 100);

5) je remet dans le clipboard
EmptyClipboard();
SetClipboardData(CF_BITMAP, hImage);

6) je referme
CloseClipboard();

mon prog ne marche pas, j'ai beau cherché pourquoi j'y arrive pas.
quelqu'un aurait une solution ou une suggestion pour m'aider ??? merci infiniment

Cheers ;)
tom

17 réponses

ymca2003 Messages postés 2070 Date d'inscription mardi 22 avril 2003 Statut Membre Dernière intervention 3 juillet 2006 7
19 mars 2004 à 21:32
je croit que c'est au niveau de la récupération du bitmap depuis le clipboard que ça coince. Voila comment récupérer le HBITMAP contenu dedans :

// ouverture du presse-papier
if(!OpenClipboard())
return;

// récupération du contenu du presse-papier (doît être une image)
HGLOBAL hData = GetClipboardData(CF_DIB);

// si la récupération a échoué, on referme le presse-papier
if(!hData)
{
CloseClipboard();
return;
}

int size = GlobalSize(hData);

LPBYTE lpData = (LPBYTE) GlobalLock(hData);

LPBITMAPINFO lpInfo= (LPBITMAPINFO) lpData;
LPBITMAPINFOHEADER	lpHeader= &lpInfo->bmiHeader;
LPBYTE lpBits = lpData + sizeof(BITMAPINFOHEADER);

HDC hdc = ::GetDC(NULL);
HBITMAP hBmp = ::CreateDIBitmap(hDC, lpHeader, CBM_INIT, lpBits, lpInfo,DIB_RGB_COLORS);
::ReleaseDC(NULL, hdc);

CloseClipboard();

0
cs_totodude Messages postés 24 Date d'inscription vendredi 19 mars 2004 Statut Membre Dernière intervention 6 avril 2004
22 mars 2004 à 11:26
Merci infiniment ymca.
j'arrive maintenant à récupérer l'objet de mon presse papier.

maintenant je voudrais découper l'image ainsi récupérée et la remttre dans le presse papier.

saurais tu quelle méthode utilisée pour ca ?
la méthode Draw de CImage réalise en fait une copie, pas une découpe.
j'arrive pas à trouver une méthode qui marche directement.

je vais donc me lancer dans le recopiage de la partie que je veux pixel par pixel, mais ca m'as l'air un peu bourrin.

la méthode clone de la classe Bitmap à l'air pas mal, mais j'arrive pas a créer un objet bitmap alors que j'inclue bien les librairies qu'il faut (gdiplus.h). J'ai une erreur de compilation comme quoi il connait pas la classe "Bitmap".

si quelqu'un connait une methode plus rapide, merci de me faire signe ;)

Cheers
0
cs_totodude Messages postés 24 Date d'inscription vendredi 19 mars 2004 Statut Membre Dernière intervention 6 avril 2004
22 mars 2004 à 14:37
alors voila ou j'en suis :

grace a ymca j'arrive à récupérer l'objet dans mon presse papier.

je pensais avoir fini le prog ensuite, mais j'ai une erreur dans les données sauvegardées dans le presse papier qui n'ont pas l'air d'être au meme format.

j'ai fait :
//définition de mon rectangle à découper
CRect myRect = new CRect(0,0,400,400);
//récupération de l'image
CImage cImage;
cImage.Attach(hBmp);

//découpage de l'image
CImage* myImage = cutImage(cImage,myRect);
HBITMAP myhBmp = myImage->Detach();

EmptyClipboard();
SetClipboardData(CF_BITMAP, myhBmp);
CloseClipboard();

ma fonction de découpe est la suivante :

CImage* cutImage(CImage cImage,CRect myRect)
{
CImage* newImage = new CImage();
newImage->Create(400,400,16,0);
CPoint ptDown,ptUp;
ptDown = myRect.BottomRight();
ptUp = myRect.TopLeft();

for(int x=0;x<((int)newImage->GetWidth());x++)
{
for(int y=0;y<((int)newImage->GetHeight());y++)
{
newImage->SetPixel(x,y,cImage.GetPixel(x,y));
}
}

return newImage;
}

pkoi ca ne marche donc pas ???

merci de votre aide
tom
0
ymca2003 Messages postés 2070 Date d'inscription mardi 22 avril 2003 Statut Membre Dernière intervention 3 juillet 2006 7
23 mars 2004 à 14:41
de la même façon que tu récupère les données les données du bitmap dans le clipboard, il ne s'agit pas d'un HBITMAP, mais une structure BITMAPINFO suivie des bits(pixels) du bitmap qu'il faut mettre dedans. En fait il s'agit n'y plus ni moins que ce que tu mettrait dans un fichier pour obtenir un *.bmp (hormis l'entête de fichier BITMAPFILEHEADER).

je vais essayer de mettre un bout de code qui fait ça.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_totodude Messages postés 24 Date d'inscription vendredi 19 mars 2004 Statut Membre Dernière intervention 6 avril 2004
23 mars 2004 à 18:33
je pensais qu'il fallait passer un handle en parametre de SetClipboardData.

si je fais
CImage cImage;
cImage.Attach(hBmp);
HBITMAP myhBmp = cImage.Detach(); //je sais c'est très con

EmptyClipboard();
SetClipboardData(CF_BITMAP, myhBmp);
CloseClipboard();



ca a l'air de marcher. mais je ne fais alors que remettre la meme image dans le presse papier. c'est pour ca que j'avais l'impression qu'il suffisait de récupérer le HBITMAP de l'image que je voulais mettre avec la méthode CImage::Detach()
mais ca ne marche pas pour une image que j'aurais modifié.

sans vouloir abuser de ta gentillesse et de ton temps, un petit bout de code m'aiderais bien, car j'arrive pas à créer la structure BITMAPINFO à passer en parametre correctement.

merci beaucoup mec.

Cheers ;)
0
ymca2003 Messages postés 2070 Date d'inscription mardi 22 avril 2003 Statut Membre Dernière intervention 3 juillet 2006 7
23 mars 2004 à 23:13
Tout d'abord, excuse pour les complications que j'ai apportées à ton problème. On peut en effet manipuler directement les HBITMAP avec le presse-papier. Le truc c'est sur la durée de vie de ceux-ci. On effet, quand tu veux récupérer le HBITMAP à partir du presse-papier, celui-ci n'est plus valable une fois que tu as appelé CloseClipboard (le propriétaire, celui qui l'a mis dedans, a peut-être libérer les ressources associées). De la même façon , il ne faut pas détruire le HBITMAP que tu met dans le presse-papier (les objets MFC englobant le GDI détruisent systématiquement les HANDLE associé dans leur destructeur). En passane par les API, c'est plus sur (de toutes façon les MFC sont fait à partir de l'API).

voici 2 fonctions :

BOOL GetClipboardBitmap(HBITMAP* phBmp, SIZE* pSize)
{
// ouverture du presse-papier
if(!OpenClipboard(NULL))
return FALSE;

// vérification du format
if(!IsClipboardFormatAvailable(CF_BITMAP))
{
CloseClipboard();
return FALSE;
}

// récupération bitmap contenu dans le presse-papier
HBITMAP hClipboardBmp = (HBITMAP) GetClipboardData(CF_BITMAP);
if(hClipboardBmp == NULL)
{
CloseClipboard();
return FALSE;
}

// récupération infos sur le bitmap (dont la taille)
BITMAP info;
SIZE size;
GetObject(hClipboardBmp, sizeof(BITMAP), &info);
size.cx = info.bmWidth;
size.cy = info.bmHeight;

// DC de l'écran
HDC hScreenDC = GetDC(NULL);

// création DC et sélection de ce bitmap dedans
HDC hClipboardDC = CreateCompatibleDC(hScreenDC);
HBITMAP hOldClipboardBmp = (HBITMAP) SelectObject(hClipboardDC, hClipboardBmp);

// création DC et bitmap en mémoire
HDC hMemDC = CreateCompatibleDC(hScreenDC);
HBITMAP hMemBmp = CreateCompatibleBitmap(hScreenDC, size.cx, size.cy);
HBITMAP hOldMemBmp = (HBITMAP) SelectObject(hMemDC, hMemBmp);

// recopie inversée
for(int x = 0; x < size.cx; x++)
{
for(int y = 0; y < size.cy; y++)
{
COLORREF color = GetPixel(hClipboardDC, x, y);
SetPixel(hMemDC, size.cx-x-1, y, color);
}
}

// sélection anciens objets et destruction DC
SelectObject(hClipboardDC, hOldClipboardBmp);
SelectObject(hMemDC, hOldMemBmp);
DeleteDC(hClipboardDC);
DeleteDC(hMemDC);
ReleaseDC(NULL, hdc);

// fermeture presse-papier
CloseClipboard();
*phBmp = hMemBmp;
pSize->cx = size.cx;
pSize->cy = size.cy;
return TRUE;
}


void SetClipboardBitmap(HBITMAP hBmp)
{
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBmp);
CloseClipboard();
}


utilisation :

int main(int argc, char* argv[])
{
HBITMAP hBmp;
SIZE size;
GetClipboardBitmap(&hBmp, &size);
SetClipboardBitmap(hBmp);
return 0;
}

le HBITMAP récupérer par la première fonction (qui est une copie inversée de ce qui se trouvait dans le presse-papier) est remis dans le presse-papier. Il n'est pas à détruire par DeleteObject (le système le fera pour nous).
0
cs_totodude Messages postés 24 Date d'inscription vendredi 19 mars 2004 Statut Membre Dernière intervention 6 avril 2004
24 mars 2004 à 18:54
Merci infiniment mec pour cette réponse plus que complete.
maintenant je vais devrais pouvoir m'en sortir.

j'ai toutefois une question supplémentaire pour finir :
j'utilise la fonction
OpenClipboard()
pour ouvrir mon clipboard, et pas OpenClipboard(NULL)
comme dans ton exemple.

je bosse sous windows avec visual studio (mon appli est un activex), et quand j'utilise ::OpenClipboard(NULL) j'ai pas d'erreur d'exécution, mais en fait le prog ne travaille pas sur le bon clip board (je récupère en faisant ctrl+v la meme image non modifiée)

quand j'utilise OpenClipboard(), j'ai une erreur à l'exécution
"Debug Assertion Failed" que j'ignore, et ensuite ca marche bien. mais je voudrais me débarrasser de cette erreur pour avoir un prog vraiment propre.

saurai tu comment corriger ca ?

merci encore pour ton aide précieuse.
bonne soirée
tom
0
ymca2003 Messages postés 2070 Date d'inscription mardi 22 avril 2003 Statut Membre Dernière intervention 3 juillet 2006 7
24 mars 2004 à 22:03
la fonction de l'API (avec :: devant) prend comme argument le HWND de la fenêtre qui sera propriétaire du presse-papier (la mainframe en générale). Si j'ai mis NULL dans mon exemple c'est que j'ai simplement testé en mode console sans créer de fenêtre et passer NULL revient à associer le presse-papier à la tâche en cours.

en MFC, il suffit d'appeler OpenCliboard() sans paramètre dans la fenêtre qui gère le presse-papier. Cette fonction appelera ensuite la fonction de l'API en lui passant le membre m_hWnd de l'objet CWnd (ou dérivé).

si tu as un Debug Assertion Failed, pose un point d'arrêt là ou tu appelle OpenClipboard et tarce les appels aux fonctions (vérifie en particulier le mebre m_hWnd de l'objet 'this', il ne doit pas être NULL).
0
cs_totodude Messages postés 24 Date d'inscription vendredi 19 mars 2004 Statut Membre Dernière intervention 6 avril 2004
30 mars 2004 à 18:23
et me revoilou !

désolé, j'étais passé à autre chose, et me revoici sur ce prog.

j'ai testé this->m_hWnd et il apparait qu'il est NULL.

j'ai essayé de bidouillé avec la fonction GetClipboardOwner()
en faisant ::OpenClipboard(GetClipboardOwner()->m_hWnd)

mais ca a pas l'air de marcher. Comment je m'en sors maintenant ? qu'est ce que je peux changer ?

ton aide est la bienvenue ;)
merci encore

Cheers ;)
tom
0
ymca2003 Messages postés 2070 Date d'inscription mardi 22 avril 2003 Statut Membre Dernière intervention 3 juillet 2006 7
1 avril 2004 à 15:08
Test en donnant le HWND de la MainFrame (AfxGetMainFrame()->m_hWnd))
0
cs_totodude Messages postés 24 Date d'inscription vendredi 19 mars 2004 Statut Membre Dernière intervention 6 avril 2004
5 avril 2004 à 17:52
Je sens qu'on touche enfin au bout !
je n'ai plus d'erreurs debug assertion, mais j'ai l'impression que mon screen shot ne marche pas.

explication : mon prog commence par

keybd_event(VK_SNAPSHOT,1,0,0);

je pensais que ca faisait le meme effet que lorsque j'appuie sur "printscreen", et en fait non. pourtant j'avais déjà tester avant. ou alors mon open clip board m'ouvre un autre presse papier que celui dans lequel la copie de l'écran est faite.

pour faire le screenshot, j'ai aussi tester le code suivant :
INPUT input[2];
::ZeroMemory(input, sizeof(input));
input[0].type = input[1].type = INPUT_KEYBOARD;
input[0].ki.wVk = input[1].ki.wVk = VK_SNAPSHOT;
input[1].ki.dwFlags = KEYEVENTF_KEYUP; // THIS IS IMPORTANT
::SendInput(2, input, sizeof(INPUT));

Pour résumer, mon prog marche sauf qu'il me fait pas la copie de l'écran (je la fait manuellement pour l'instant en appuyant sur le bouton)

j'espère que tu vois le pb. merci infiniment en tout cas pour toute ton aide.

Cheers ;)
tom
0
cs_totodude Messages postés 24 Date d'inscription vendredi 19 mars 2004 Statut Membre Dernière intervention 6 avril 2004
5 avril 2004 à 18:30
en fait je viens tester à nouveau mon appli.

j'ai l'impression que seule la découpe est réalisé, sur l'image déjà dans le presse papier (et pas celle du screen shot de l'écran en cours).

du coup j'ai mis au début du prog :
::OpenClipboard(AfxGetMainWnd()->m_hWnd); EmptyClipboard();
CloseClipboard();

pour vider mon presse papier.
et dans ce cas, il n'y a plus que l'impression écran qui est réalisé, et plus la découpe qui devrait venir après !!!

je n'y comprend donc plus rien. c'est a se tirer les cheveux se truc (et je reste poli !)

merci de toutes suggestions qui pourrait me sortir de la

Cheers ;)
tom
0
ymca2003 Messages postés 2070 Date d'inscription mardi 22 avril 2003 Statut Membre Dernière intervention 3 juillet 2006 7
5 avril 2004 à 23:11
Si je résume ton pb, tu réalise une capture d'écran que tu divise par la suite.

pour cela, fait la capture toi-même en récupérant le DC de l'écran et on le recopiant dans un bitmap.

pour récupérer le DC de l'écran :
HDC hScreenDC = GetDC(NULL);
0
cs_totodude Messages postés 24 Date d'inscription vendredi 19 mars 2004 Statut Membre Dernière intervention 6 avril 2004
6 avril 2004 à 18:48
C'mon dude, ca marche !

en fait la nouvelle impression écran n'était pas encore enregistrée dans le presse papier avant que je découpe, et je récupèrais donc dans mon presse papier l'image d'avant.

j'ai donc du ruser pour faire patienter le prog le temps que l'enregistrement se fasse.
j'ai fais :

keybd_event(VK_SNAPSHOT,1,0,0);
keybd_event(VK_RETURN,1,0,0);
AfxMessageBox("pause");

la premiere ligne réalise mon impression écran, les 2 secondes ouvre une boite de dialogue qui se ferme direct, et qui laisse le temps de faire l'enregistrement. bizarre quand meme.
j'ai essayer d'utiliser la fonction sleep, mais ca fait juste que tout le prog attend, et après l'enregistrement se faisait pas quand meme.

Résumé : j'ai un prog qui marche, meme si il est pas très clean. merci beaucoup pour ton aide mec, jamais je m'en serais sorti sans toi.

Cheers ;)
tom
0
cs_werty Messages postés 1 Date d'inscription mardi 18 février 2003 Statut Membre Dernière intervention 15 mai 2004
15 mai 2004 à 23:32
Un truc pour ne pas avoir besoin de faire apparaître et disparaître un MessageBox:

bool bBoucle = TRUE;
do
{
WaitForSingleObject(NULL, 20);
if(IsClipboardFormatAvailable(CF_DIB))
bBoucle = FALSE;
}
while(bBoucle == TRUE);

Le prog attend 20 ms avant chaque vérification jusqu'a ce que le bmp soit rendu dans le clipboard

En passant si quelqu'un a un moyen de le convertir en jpg (je connais ijl mais comment l'utiliser dans un programme console?) ou simplement d'enregistrer le bitmap dans un fichier faites moi signe
0
Oeil_de_taupe Messages postés 150 Date d'inscription samedi 31 janvier 2004 Statut Membre Dernière intervention 16 février 2009
3 avril 2005 à 16:58
Il y a aussi l'API SetClipboardViewer pour être averti d'un changemant
du presse-papier (je vous laisser regarder la doc sur MSDN)

La taupe voit toujours tout (sauf les bugs)
0
chichif Messages postés 2 Date d'inscription mercredi 20 avril 2005 Statut Membre Dernière intervention 25 avril 2005
25 avril 2005 à 10:47

0
Rejoignez-nous