Letzte Änderung: 2.11.97 von B. Tritsch
Zurück zum Index "PC- und MS-Windows-Support"
Die Zwischenablage, im englischen Clipboard genannt, ist eine systemweite einmalige Ressource, auf die alle Windows-Programme zugreifen können, wenn sie mit anderen Applikationen Daten austauschen möchten. Mit Windows 3 ist das Clipboard dahingehend erweitert worden, daß es auch mit Standard-MS-DOS-Programmen einen Zugriff erlaubt.
Ein Datenaustausch unter Windows-Applikationen erstreckt sich über die folgenden Formate:
| CF_OEMTEXT | Text mit OEM-Zeichensatz |
| CF_TEXT | Standardtextformat unter Windows (ANSI-Zeichensatz). Jede Zeile wird mit CR-LF abgeschlossen, das Textende wird mit dem NULL-Zeichen gekennzeichnet |
| CF_UNICODETEXT | Text mit Unicode-Zeichensatz |
| CF_BITMAP | Device-dependent Bitmap |
| CF_DIB | Device-independent Bitmap. Daten liegen in einer Form vor, wie sie durch die BITMAPINFO-Windows-Datenstruktur vorgegeben wird |
| CF_TIFF | Tagged Image File Format |
| CF_ENHMETAFILE | Erweitertes Metafile |
| CF_METAFILEPICT | Windows Metafile |
| CF_RIFF | Resource Interchange File Format |
| CF_WAVE | Standard Wave File Format (Audio) |
| CF_DIF | Data Interchange Format von Software Arts |
| CF_PALETTE | Farbpaletten-Format |
| CF_PENDATA | Microsoft Pen Extension Data |
| CF_SYLK | Microsoft Symbolic Link Format |
Zudem gibt es private Clipboard-Datenformate, bei denen die Daten mit applikationsspezifischen Attributen versehen sind. Weiterhin gibt es private Datenformate, die vor einem allgemeinen Zugriff schützen sollen. Zuletzt ist es möglich, Datenformate zu spezifizieren, die nur innerhalb eines bestimmten Betriebssystems (z.B. Windows 95) oder einer Applikation anwendbar sind. Hierfür gibt es eine Reihe von freien Clipboard-Kennziffern.
Die unterstützten Clipboard-Operationen sind:
Um Daten zum Clipboard zu transferieren, muß eine Applikation zwei Dinge tun: Das Datenobjekt muß allokiert werden (GlobalAlloc) und das Eigentümerrecht am Clipboard muß erteilt werden (OpenClipboard, EmptyClipboard, SetClipboardData, CloseClipboard).
Um Daten aus dem Clipboard zu bekommen, kann eine Applikation die Funktion IsClipboardFormatAvailable verwenden, um herauszufinden, ob Daten in einem bestimmten Format im Clipboard verfügbar sind. Wenn sie die Daten dann kopieren möchte, ruft sie OpenClipboard gefolgt von GetClipboardData. Am Ende wird der Zugriff auf das Clipboard durch CloseClipboard beendet.
Eine einfache Implementation eines Clipboard-Zugriffs mit den Optionen Cut, Copy, Paste und Delete wird im folgenden Beispiel-Code demonstriert. Hierbei kann die Anfangs vorhandene Zeile in der Client Area in das Clipboard transferiert werden oder aber ein Textstring aus dem Clipboard in die Client Area der Beispielanwendung.
Sourcecode HELLOCF.C
#include <windows.h>
#include "hellocf.h"
HINSTANCE hInstance;
char *pszData;
void DrawHello(HWND hwnd)
{
HDC hDC;
PAINTSTRUCT paintStruct;
RECT clientRect;
if (pszData != NULL)
{
hDC = BeginPaint(hwnd, &paintStruct);
if (hDC != NULL)
{
GetClientRect(hwnd, &clientRect);
DPtoLP(hDC, (LPPOINT)&clientRect, 2);
DrawText(hDC, pszData, -1, &clientRect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, &paintStruct);
}
}
}
void CopyData(HWND hwnd)
{
HGLOBAL hData;
LPVOID pData;
OpenClipboard(hwnd);
EmptyClipboard();
hData = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
strlen(pszData) + 1);
pData = GlobalLock(hData);
strcpy((LPSTR)pData, pszData);
GlobalUnlock(hData);
SetClipboardData(CF_TEXT, hData);
CloseClipboard();
}
void DeleteData(HWND hwnd)
{
free(pszData);
pszData = NULL;
InvalidateRect(hwnd, NULL, TRUE);
}
void PasteData(HWND hwnd)
{
HANDLE hData;
LPVOID pData;
if (!IsClipboardFormatAvailable(CF_TEXT)) return;
OpenClipboard(hwnd);
hData = GetClipboardData(CF_TEXT);
pData = GlobalLock(hData);
if (pszData) DeleteData(hwnd);
pszData = malloc(strlen(pData) + 1);
strcpy(pszData, (LPSTR)pData);
GlobalUnlock(hData);
CloseClipboard();
InvalidateRect(hwnd, NULL, TRUE);
}
void SetMenus(HWND hwnd)
{
EnableMenuItem(GetMenu(hwnd), ID_EDIT_CUT,
pszData ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(GetMenu(hwnd), ID_EDIT_COPY,
pszData ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(GetMenu(hwnd), ID_EDIT_PASTE,
IsClipboardFormatAvailable(CF_TEXT) ?
MF_ENABLED : MF_GRAYED);
EnableMenuItem(GetMenu(hwnd), ID_EDIT_DELETE,
pszData ? MF_ENABLED : MF_GRAYED);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_FILE_EXIT:
DestroyWindow(hwnd);
break;
case ID_EDIT_CUT:
CopyData(hwnd);
DeleteData(hwnd);
break;
case ID_EDIT_COPY:
CopyData(hwnd);
break;
case ID_EDIT_PASTE:
PasteData(hwnd);
break;
case ID_EDIT_DELETE:
DeleteData(hwnd);
break;
}
break;
case WM_PAINT:
DrawHello(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_INITMENUPOPUP:
if (LOWORD(lParam) == 1)
{
SetMenus(hwnd);
break;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR d3, int nCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASS wndClass;
HANDLE hAccTbl;
pszData = malloc(14);
strcpy(pszData, "Hello, World!");
hInstance = hThisInstance;
if (hPrevInstance == NULL)
{
memset(&wndClass, 0, sizeof(wndClass));
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = "HelloMenu";
wndClass.lpszClassName = "Hello";
if (!RegisterClass(&wndClass)) return FALSE;
}
hwnd = CreateWindow("Hello", "Hello",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
hAccTbl = LoadAccelerators(hInstance, "HelloMenu");
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(hwnd, hAccTbl, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
Headerfile HELLOCF.H
#define ID_FILE_EXIT 1000 #define ID_EDIT_CUT 1001 #define ID_EDIT_COPY 1002 #define ID_EDIT_PASTE 1003 #define ID_EDIT_DELETE 1004
Ressourcefile HELLOCF.RC
#include "windows.h"
#include "hellocf.h"
HelloMenu MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_FILE_EXIT
END
POPUP "&Edit"
BEGIN
MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT, GRAYED
MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY, GRAYED
MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE, GRAYED
MENUITEM "&Delete\tDel", ID_EDIT_DELETE, GRAYED
END
END
HelloMenu ACCELERATORS
BEGIN
"X", ID_EDIT_CUT, VIRTKEY, CONTROL
"C", ID_EDIT_COPY, VIRTKEY, CONTROL
"V", ID_EDIT_PASTE, VIRTKEY, CONTROL
VK_DELETE, ID_EDIT_DELETE, VIRTKEY
END
Im Gegensatz zu Druckern, die typischerweise über die parallele Schnittstelle oder das Netzwerk angesprochen werden, ist die Verwendung der seriellen Schnittstelle etwas problematischer. Es kann bei den Geräten, die an die serielle Schnittstelle angebunden werden, kein kleinster gemeinsamer Nenner gefunden werden. Sie sind zu verschieden. Aus diesem Grund gibt es unter Windows keinen High-Level-Support wie für Drucker für die serielle Schnittstelle. Dafür gibt es eine Reihe von Low-Level-Funktionen, die den Entwickler mit Basisfunktionalitäten für entsprechende Geräte (Modems, Meßinstrumente) versorgen.
Wie es unter Windows üblich ist, überprüft auch bei der seriellen Schnittstelle nicht das Anwendungsprogramm in einer Schleife das Eintreffen von Daten, sondern wird dies über einen Interrupt-getriebenen Mechanismus (Windows-Messages) abgehandelt. Die Daten werden dafür so lange in einem Eingangspuffer zwischengespeichert, wo sie das Programm beim Abarbeiten des Interrupts abholen kann.
Der Eingangspuffer wird "Receive Data Queue" genannt. Die Daten akkumulieren in der Queue, wenn sie empfangen werden. Möchte eine Windows-Applikation auf die Daten zugreifen möchte, dann liest sie diese über die Queue. Daten, die abgeschickt werden sollen, werden analog in einen Ausgangspuffer geschrieben, die "Transmit Data Queue".
Die gängigste Methode, den Eingangspuffer zu überprüfen, ist in der Applikations-Queue nach der entsprechenden Message zu suchen. Die zugehörige API-Funktion ist PeekMessage. Diese Funktion hatte unter Windows 3.x den Vorteil, daß sie nur dann auf die Nachrichtenschleife zugreift, wenn sonst nichts im Gesamtsystem geschieht, d.h. die Idle-Zeiten ausnutzt. Zudem erlaubt PeekMessage gezielt den Zugriff auf bestimmte Message-Typen.
Für den Zugriff auf die serielle Schnittstelle muß über den Device Control Block (DCB) erfolgen. Er ist ein wenig mit dem Device Context für GDI-Funktionen zu vergleichen. Der DCB enthält Parameter wie Baud Rate, Byte Size, Parity, Stop Bits, Timeouts, Handshakes, Xon/Xoff, etc. Im folgenden werden die wichtigsten Funktionen für die serielle Schnittstelle kurz vorgestellt:
Diese Funktionen sind jedoch eher für eine 16-Bit-Umgebung geeignet. Für 32-Bit-Applikationen werden serielle Kommunikations-Ports mit Dateien assoziiert. Hierfür wird der Zugriff auf die serielle Schnittstelle mit dem File-API abgehandelt. Das Öffnen des seriellen Ports geschieht mit der CreateFile-Funktion.
HANDLE hComm;
hComm = CreateFile( gszPort,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if (hComm == INVALID_HANDLE_VALUE)
// error opening port; abort
Das Lesen und Schreiben zu einem Kommunikations-Port ist dem File I/O sehr ähnlich. Hierbei wird zwischen asynchronem und synchronem bzw. overlapped und nonoverlapped I/O unterschieden, wobei ersteres zwar leistungsfähiger aber auch komplizierter ist.
Die gängige Art ist die synchrone I/O. Sie hat zwar einige Beschränkungen, z.B. ist der aufrufende Prozeß blockiert während der I/O-Operation. Jedoch ist die Programmierung mit den Funktionen ReadFile und WriteFile recht einfach ist.
Es gibt zwei Methoden, um den Status eines Kommunikations-Ports zu ermitteln. Die SetCommMask-Funktion setzt eine Event-Maske wie bei der 16-Bit-Funktion SetCommEventMask. Die WaitCommEvent-Funktion wartet auf das Auftreten des gewünschten Ereignisses, was der 16-Bit-Funktion EnableCommNotification ähnelt.
Wie oben schon beschrieben, muß auch in einer 32-Bit-Applikation der Device Control Block gesetzt werden. Dies geschieht mit einer der drei API-Funktionen:
GetCommState
DCB dcb = {0};
if (!GetCommState(hComm, &dcb))
// Error getting current DCB settings
else
// DCB is ready for use.
BuildCommDCB
DCB dcb;
FillMemory(&dcb, sizeof(dcb), 0);
dcb.DCBlength = sizeof(dcb);
if (!BuildCommDCB("9600,n,8,1", &dcb)) {
// Couldn't build the DCB. Usually a problem
// with the communications specification string.
return FALSE;
}
else
// DCB is ready for use.
SetCommState
DCB dcb;
FillMemory(&dcb, sizeof(dcb), 0);
if (!GetCommState(hComm, &dcb)) // get current DCB
// Error in GetCommState
return FALSE;
// Update DCB rate.
dcb.BaudRate = CBR_9600 ;
// Set new state.
if (!SetCommState(hComm, &dcb))
// Error in SetCommState. Possibly a problem with the communications
// port handle or a problem with the DCB structure itself.
Die Microsoft Developer Network Library liefert speziell für die serielle Kommunikation unter Win32 weiterführende Artikel.