Entwickler-Ecke

Windows API - Zwischenablage (Bitmap) in anderes Programm (Paint)einfügen?


Tony-S - Mo 19.07.10 16:31
Titel: Zwischenablage (Bitmap) in anderes Programm (Paint)einfügen?
Hallo Forum, ich möchte aus meiner Zwischenablage eine Bitmap in ein anderes Programm (Paint oder Word) einfügen.

Als erstes suche ich nach dem "Paint" Fenster, was auch anscheinend eintritt, da Window > 0 ist. Dann simuliere ich Strg + V, also einfügen.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
procedure SendBMPtoPaint(BMP: TBitmap);
var
  Window: HWND;
begin
  Window := FindWindowEx(FindWindow('Paint'nil), 00nil);
  if Window > 0 then
  begin
  PostMessage(Window, wm_KeyDown, vk_Control,0);
  PostMessage(Window, wm_KeyDown, Ord('V'),0);
  PostMessage(Window, wm_Keyup, vk_Control,0);
  PostMessage(Window, wm_Keyup, Ord('V'),0);
  end;
end;

Diese Prozedur rufe ich dann auf:

procedure TForm1.btnPaintExportClick(Sender: TObject);
begin
  Clipboard.Assign(imgBMP.Picture.Bitmap);
  //Zuweisung der Bitmap in die Zwischenablage
  Try
  ShellExecute(Application.Handle,'open', pchar('mspaint.exe'),nilNil, SW_Show);
  //Paint öffnen
  Finally
  SendBMPtoPaint(imgBMP.Picture.Bitmap);
  //Bitmap einfügen?
end;
end;


Was mach ich da falsch, oder was kann ich da anders machen? Bin für jede Hilfe dankbar.

Moderiert von user profile iconNarses: Überflüssige Zeilenumbrüche/Leerzeilen entfernt.
Moderiert von user profile iconNarses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Mo 19.07.2010 um 16:33


Tastaro - Mo 19.07.10 17:32

Probier mal

Delphi-Quelltext
1:
2:
3:
4:
  PostMessage(Window, wm_KeyDown, vk_Control,0);
  PostMessage(Window, wm_KeyDown, Ord('V'),0);
  PostMessage(Window, wm_Keyup, Ord('V'),0);
  PostMessage(Window, wm_Keyup, vk_Control,0);


Beste Grüße


glotzer - Mo 19.07.10 17:37

eventuell auch ein sleep(1000) einfügen befor der tastendruck simuliert wird, ich vermute mal paint ist noch nicht ganz gestartet...


MaPsTaR - Mo 19.07.10 18:30

Hast du schonmal geprüft, was dir FindWindow('Paint'nil) zurück gibt?
Ich wette mit dir, da sollte 0 herauskommen.

Wer weiß welches Fenster du mit deinem Code ansprichst...

Wenn du dir die Parameter von FindWindow ansiehst wirst du auch merken warum:

HWND FindWindow(

LPCTSTR lpClassName, // pointer to class name --> PChar('MSPaintAPP')
LPCTSTR lpWindowName // pointer to window name --> klappt mit nil
);


Versuch mal das:


Delphi-Quelltext
1:
  FindWindow(PChar('MSPaintAPP'), nil);                    


damit solltest du das richtige Handle von dem Fenster bekommen.


Tony-S - Mo 19.07.10 20:21

Vielen Dank schon mal, man muss also die Tasten in der entgegen gesetzten Richtung "loslassen".
Sleep sowie der Tipp von Mapstar haben leider nicht so ganz geklappt - Window.
Ergänzt hab ich noch:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
procedure TForm1.FormCreate(Sender: TObject);
// BMP Global
begin
BMP:= TBitmap.Create;
BMP.Width := imgBMP.Width;
BMP.Height := imgBMP.Height;
BMP := imgBMP.Picture.Bitmap;
end;

// Das hätte glaube so oder so noch dazu gehört?

procedure TForm1.btnPaintExportClick(Sender: TObject);
begin
  Clipboard.Assign(imgBMP.Picture.Bitmap);
  Try
  ShellExecute(Application.Handle,'open', pchar('mspaint.exe'),nilNil, SW_Show);
  Finally
  SendBMPtoPaint(BMP);
end;
end;

procedure SendBMPtoPaint(BMP: TBitmap);
var
  Window: HWND;
begin
//Window := FindWindowEx(FindWindow('Paint', nil), 0, 0, nil);<--vorherige
Window := FindWindow(PChar('MSPaintAPP'), nil); <-- 

//mit der Methode bekomm ich 0 zurück, mit meiner vorherigen eine 5 stellige Zahl,
//aber auch mit meiner bringen die Änderung nix, hab ich irgendwas übersehen?
  if Window > 0 then
  begin
  sleep(2000);
  PostMessage(Window, wm_KeyDown, vk_Control,0);
  PostMessage(Window, wm_KeyDown, Ord('V'),0);
  PostMessage(Window, wm_Keyup, Ord('V'),0);
  PostMessage(Window, wm_Keyup, vk_Control,0);
  end;
end;


Ich hab btw Win7, aber das wird wohl nix ausmachen.


MaPsTaR - Mo 19.07.10 21:21

Lad dir mal dieses Programm [http://mh-nexus.de/de/downloads.php?product=PropEdit] runter, damit kannst die Fenster einer Anwendung auswerten, dort erhältst du z.B. auch den Klassennamen von dem Fenster.

MSPaintApp ist der KlassenName für das Fenster von Paint unter XP, vielleicht liegt es daran.

Unter XP kommst du so zu der Zeichenfläche, hab das mit dem oben genannten Programm getestet, ob die Handle stimmen.


Delphi-Quelltext
1:
2:
3:
  Window1 := FindWindow(PChar('MSPaintAPP'), nil);
  Window2 := FindWindowEx(Window1, 0, PChar('AfxFrameOrView42u'), PChar(''));
  Window3 := FindWindowEx(Window2, 0, PChar('Afx:1000000:8'), PChar(''));


Deinen Code habe ich mal ausprobiert. Im Paint funktioniert das bei mir auch nicht, dafür aber in Edit-Feldern, wenn ich es so mache:

Delphi-Quelltext
1:
2:
3:
4:
5:
    PostMessage(Window, WM_KEYDOWN, vk_Control, 0);
    PostMessage(Window, WM_KEYDOWN, Ord('V'), 0);
    PostMessage(Window, WM_CHAR, 220);
    PostMessage(Window, WM_KEYUP, Ord('V'), LongInt($C0000001));
    PostMessage(Window, WM_KEYUP, vk_Control, LongInt($C0000001));


Vielleicht will Paint noch eine spezielle Message, um Strg+V zu verwerten??


Tony-S - Mo 19.07.10 23:36

Danke für das Programm, hatte auch mal sowas ähnliches. Also an 7 liegt es nicht, hier heißt es auch MSPaintApp.
Das Programm gibt mir ja auch ein Handle als Hexwert(?) zurück und zwar 1061A, was Dezimal 67098. Wenn ich mein Programm jetzt starte und nachschaue welchen Wert das Handle des Paint-Fensters hat, so wird mir da 263568 als Wert zurückgegeben :/
Das mit Edit-Feldern funktioniert heißt ja nur, das Paint anscheinend wirklich noch was zusätzliches braucht, nur was?! Davon mal abgesehen, gibt es nicht noch eine andere (unkomplizierte) Lösung für mein Problem? Wobei ich ja schon der Meinung bin es so richtig zu lösen wäre :D.

Ist ja auch für das forum interessant wenn wir eine Lösung finden, aber vorallem für mich.


MaPsTaR - Di 20.07.10 00:22

Wieso das mit dem Handle nicht klappt kann ich nicht sagen, evtl. deinen aktuellen Code nochmal posten?

Wenn du das Bild nur in Paint öffnen willst, könntest du das Bild über dein Programm speichern und dann mit Paint öffnen lassen...


Tony-S - Di 20.07.10 00:49


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
procedure TForm1.FormCreate(Sender: TObject);
begin
BMP:= TBitmap.Create;
BMP.Width := imgBMP.Width;
BMP.Height := imgBMP.Height;
BMP := imgBMP.Picture.Bitmap;
end;

procedure SendBMPtoPaint(BMP: TBitmap);
var
  Window: HWND;
begin
Window := FindWindow(PChar('MSPaintApp'), nil);

  if Window > 0 then
  begin
  sleep(2000);
  PostMessage(Window, wm_KeyDown, vk_Control,0);
  PostMessage(Window, wm_KeyDown, Ord('V'),0);
  PostMessage(Window, wm_Keyup, Ord('V'),0);
  PostMessage(Window, wm_Keyup, vk_Control,0);
  end;
end;

procedure TForm1.btnPaintExportClick(Sender: TObject);
begin
  Clipboard.Assign(imgBMP.Picture.Bitmap);
  Try
  ShellExecute(Application.Handle,'open', pchar('mspaint.exe'),nilNil, SW_Show);
  Finally
  SendBMPtoPaint(BMP);
end;
end;



Nee das mit dem erst speichern macht sich in dem Programm in dem ich das brauche nicht wirklich gut.
´Vielleicht is ja auch das, was ich in der Prozedur mache Blödsinn, also das die eine bmp erwartet hmmm.


Sinspin - Di 20.07.10 09:22

Bist du dir sicher das es einfach reicht das Bitmap zur Zwischenablage zu "Assignen"? Ich habe bisher noch nie was brauchbares gefunden um Bilder in die Zwischenablage einzufügen, aber soweit ich weis muss das Bild im Metafile Format sein.
Wenn du dazu Informationen gefunden hast wäre ich für die Quelle dankbar.


Flamefire - Di 20.07.10 10:58

kontrolliere mal, was in der Zwischenablage steht. Ich vermute dass es nur der pointer (also das objekt) auf die bitmap ist


ALF - Di 20.07.10 11:52

hi, ich weiss nicht ob Du es schon gemacht hast. Aber soviel ich weiss must Du dafür ein Handle erstellen wenn Du die Zwischenablage global verwenden willst.
Alles ander was man inerhalb von Delphi macht, bleibt auch in Delphi selbst.
Musste ich bei mir auch machen, allerdings ging es bei mir um dragdrop von Files aus meinem Programm herraus. Also strg+c dann strg+v z.B.
Bei Dir währe es BitMap was Du ja einfügen tust. Ist aber vom Prinzip her das gleiche, lediglich der Aufruf würde sich ändern!

Delphi-Quelltext
1:
Clipboard.SetAsHandle(CF_HDROP, hGlobal);                    

für CF_HDROP gibt es CF_BITMAP glaube ich!

Hoffentlich habe ich es richtig erklärt!

Gruss Alf


Tony-S - Di 20.07.10 12:10

@Sinspin, klappen tut das aufjeden Fall, denn wenn ich das mal so ausprobiere,
Paint öffne und dann (selbser) Strg + V drücke wird das Bild eingefügt, sprich es war auch in der Zwischenablage.
Auf der Suche nach einer Quelle hab ich noch das hier gefunden:

http://www.swissdelphicenter.ch/de/showcode.php?id=1981

Da gibt es die Prozedur "PasteBitmap32FromClipboard" als Ziel erwartet die auch ne Bitmap..


@Flamefire hm wie überprüft man das :D? Ich mein es scheint ja "da" zu sein.
Ist wohl doch alles komplizierter als ich dachte ;D.


Hidden - Di 20.07.10 12:26

Hi :)

Wenn es jetzt nicht um den allgemeinen Fall, sondern konkret um Paint geht: Kannst du die Datei nicht von deinem Programm abspeichern(TBitmap.SaveToFile/Stream), und dann per Parameter mit Paint öffnen?

lg,


Tony-S - Di 20.07.10 12:27

@ ALF

Hm nein das habe ich nicht.
Ich hab zumindestens verstanden wormu es geht :D,
der erste Paramater ist der Dateityp das dürfte auch CF_Bitmap sein,
der zweite ist dann wie darauf zugegriffen werden soll?

Sprich das sieht dann so aus:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TimgBMP.btnPaintExportClick(Sender: TObject);
begin
Clipboard.SetAsHandle(CF_BITMAP, hGlobal);
// hGlobal scheint aber noch irgendetwas zu erwarten <--

Clipboard.Assign(imgBMP.Picture.Bitmap);
  Try
  ShellExecute(Application.Handle,'open', pchar('mspaint.exe'),nilNil, SW_Show);
  Finally
  SendBMPtoPaint(imgBMP.Picture.Bitmap);
end;
end;


Siehe Zeile mit Pfeil


ALF - Di 20.07.10 12:47

hglobel ist der Handler dafür!

Delphi-Quelltext
1:
2:
3:
4:
var
   hGlobal: THandle;
begin
hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,

der allerdings dannn Deiner Bitmap noch zugewiessen werden muss.
Ich weiss zwar nicht ob der Umweg nötig is,t da Du ja selber sagst, mit STRG+V kannst Du die Bitmap ja schon in MSPAINT einfügen.

Ansonsten empfehle ich es so wie user profile iconHidden es vorgeschlagen hat.Speichern musst Du sie sicherlich irgendwan!
Denn, entweder Du hängst die Datei beim Aufruf von MSPAINT mit ran, oder Du must sowieso mit STRG+V das BitMap in MSPAINT einfügen!!! Und dies funktioniert ja schon, wie Du sagst. Ich glaube nicht das es da noch andere Möglichkeiten gibt!

Gruss Alf


MaPsTaR - Di 20.07.10 12:49

Hallo, ich glaube, das Problem liegt nicht bei der Zuweisung zur Zwischenablage, sondern bei der Übergabe der Tastenkombination an Paint.

Probier mal das:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.SendBMPtoPaint;
var
  Window: HWND;
begin
  Application.ProcessMessages;
  Sleep(500);
  Window := FindWindow(PChar('MSPaintAPP'), nil);
  if Window > 0 then
  begin
    Sleep(500);
    SetForegroundWindow(Window);
    Sleep(500);
    keybd_event(vk_Control, MapVirtualKey(vk_Control, 0), 00);
    keybd_event(vkKeyScan('V'), MapVirtualKey(vkKeyScan('V'), 0), 00);
    keybd_event(vkKeyScan('V'), MapVirtualKey(vkKeyScan('V'), 0), KEYEVENTF_KEYUP, 0);
    keybd_event(vk_Control, MapVirtualKey(vk_Control, 0), KEYEVENTF_KEYUP, 0);
  end;
end;


Bei mir läuft es so, eventuell musst du die sleep's noch etwas anpassen.

Gruß MaPsTaR


ALF - Di 20.07.10 12:57

user profile iconMaPsTaR hat folgendes geschrieben Zum zitierten Posting springen:
Hallo, ich glaube, das Problem liegt nicht bei der Zuweisung zur Zwischenablage, sondern bei der Übergabe der Tastenkombination an Paint.

Das vermute ich auch!
Wobei ich mich frage was das Sleep soll!
Die Procedure muss sowie so komplet durchlaufen werden bevor irgend etwas passiert!
Wenn dann müssten es zwei Aufrufe sein!
Der erste öffnet Paint!
Der zweite übergibt an das Handle von Paint die Tasten!!

Gruss ALf


MaPsTaR - Di 20.07.10 12:59

Das würde natürlich auch gehen, aber so wie Tony-S es angefangen hat habe ich es ohne Sleep probiert und da ist gar nichts passiert.

EDIT:
Die Werte für Sleep lassen sich bestimmt auch noch runtersetzen, das sollte von dem jeweiligen System abhängen, wie lange Paint braucht, um die Eingabe empfangen zu können.
Hatte jetzt bloß keine Lust das auch noch auszuprobieren. :-)


Tony-S - Di 20.07.10 13:08

Danke Mapstar das läuft!

Aber da ich nicht kopieren will, naja gut mach ich jetzt eh ^^, wäre es noch nett wenn du mir bissl was erklärst:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
procedure TForm1.SendBMPtoPaint;
var
  Window: HWND;
begin
  Application.ProcessMessages;
  // das macht was?
  Sleep(500);
  Window := FindWindow(PChar('MSPaintAPP'), nil);
  if Window > 0 then
  begin
    Sleep(500);
    SetForegroundWindow(Window);
    // das setzt wohl den Fokus auf das vorderste Fenster ja?
    Sleep(500);
    wo ist der Unterschied mit MapVirtualKey?
    keybd_event(vk_Control, MapVirtualKey(vk_Control, 0), 00);
    keybd_event(vkKeyScan('V'), MapVirtualKey(vkKeyScan('V'), 0), 00);
    keybd_event(vkKeyScan('V'), MapVirtualKey(vkKeyScan('V'), 0), KEYEVENTF_KEYUP, 0);
    keybd_event(vk_Control, MapVirtualKey(vk_Control, 0), KEYEVENTF_KEYUP, 0);
  end;
end;



Ansonsten Vielen Dank an alle hier die dazu beigetragen haben, das Problem zu lösen.
Schönen Tag noch.


ALF - Di 20.07.10 13:11

Mit Sleep würde ich das nicht empfehlen! Vom Systhem abhängig! langsamer rechner langsamer aufbau von Paint
Wenn, dann warten bis das Handle von Paint da ist oder, komm auf den Begriff nicht, bis Paint onshow ist.
Dann erst die Tasten Übergabe machen! Würde ich so vorschlagen!

Gruss Alf


MaPsTaR - Di 20.07.10 13:14

Zitat:
Mit der Methode ProcessMessages können Sie die Ausführung einer Anwendung unterbrechen, sodass die Botschaftswarteschlange verarbeitet werden kann.

Das kannst du hier wahrscheinlich auch weglassen, ist eher in Schleifen wichtig, wenn während des Schleifendurchlaufs etwas abgearbeitet werden muss.

Zitat:
SetForegroundWindow(Window);
setzt Window als oberstes Fenster, damit es die Tastaturbotschaften empfängt, da man mit keybd_event kein Handle ansprechen kann.

Zitat:
The MapVirtualKey function translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a virtual-key code.

gibt den ScanCode einer Taste zurück, das wird für keybd_event benötigt, genauer kann ich das jetzt auch nicht erklären.

EDIT:

@ALF


Delphi-Quelltext
1:
2:
3:
4:
5:
  Window := 0;
  repeat
    Window := FindWindow(PChar('MSPaintAPP'), nil);
    Application.ProcessMessages;
  until Window <> 0;


Mit dieser Änderung können alle Sleep's rausgenommen werden.


elundril - Di 20.07.10 13:17

darf ich mal auf diesen [http://www.delphi-library.de/viewtopic.php?t=93676] Library-Eintrag von user profile iconBenBE hinweisen? Zum Thema "man weiß nicht wann das Programm gestartet ist".

lg elundril