Мне нужно передать строку длиннее 32/64 КБ в буфер обмена из моей программы, и, поскольку встроенная функция CLIPBOARD в OpenEdge имеет ограничение, я должен прибегнуть к использованию вызовов DLL.
Странно то, что все работает нормально... один раз... но если я пытаюсь сделать это дважды в программе, то программа вылетает. Я использую OpenEdge 11.3.1, а также пробовал его в версии 10.2B, которая работает лучше, но выдает другое сообщение о сбое.
Я пытался перемещать вещи, не очищая буфер обмена (согласно MS, я не должен очищать, но без очистки он не работает), изменяя функцию OpenClipboard для использования CURRENT-WINDOW:HWND вместо 0, и ничего не меняется.
Как я уже сказал, все работает нормально один раз, и буфер обмена заполнен моим текстом ... но если я снова попытаюсь открыть ту же программу в OpenClipboard, она обязательно вылетит.
После прочтения руководства по API гребешком с мелкими зубьями я думаю, что нашел проблему:
После вызова SetClipboardData система становится владельцем объекта, указанного параметром hMem. Приложение может читать данные, но не должно освобождать дескриптор или оставлять его заблокированным, пока не будет вызвана функция CloseClipboard. (Приложение может получить доступ к данным после вызова CloseClipboard). Если параметр hMem идентифицирует объект памяти, объект должен быть выделен с помощью функции GlobalAlloc с флагом GMEM_MOVEABLE.
Я не знаю, есть ли в OpenEdge способ выделить глобальную память, поэтому я в тупике. Если я просто не отпущу указатель памяти, то смогу снова открыть буфер обмена, но не смогу повторно использовать переменную, поскольку Progress не понимает, что переменная больше не принадлежит ему. Второй SET-SIZE не имеет никакого эффекта, хотя mRet является локальной переменной в функции, похоже, что она не сбрасывается при каждом вызове функции.
/* Clipboard Crash Test */
ROUTINE-LEVEL ON ERROR UNDO, THROW.
SESSION:ERROR-STACK-TRACE = TRUE.
PROCEDURE OpenClipboard EXTERNAL 'user32.dll':
DEFINE INPUT PARAMETER hWndNewOwner AS LONG NO-UNDO.
DEFINE RETURN PARAMETER lRet AS LONG NO-UNDO.
END PROCEDURE.
PROCEDURE CloseClipboard EXTERNAL 'user32.dll':
DEFINE RETURN PARAMETER lRet AS LONG NO-UNDO.
END PROCEDURE.
PROCEDURE EmptyClipboard EXTERNAL 'user32.dll':
DEFINE RETURN PARAMETER lRet AS LONG NO-UNDO.
END PROCEDURE.
PROCEDURE SetClipboardData EXTERNAL 'user32.dll':
DEFINE INPUT PARAMETER uFormat AS LONG NO-UNDO.
DEFINE INPUT PARAMETER hMem AS LONG NO-UNDO.
DEFINE RETURN PARAMETER uRet AS LONG NO-UNDO.
END PROCEDURE.
FUNCTION SetClipboardText RETURNS LOGICAL (cText AS LONGCHAR):
DEFINE VARIABLE iRet AS INT64 NO-UNDO.
DEFINE VARIABLE mRet AS MEMPTR NO-UNDO.
DEFINE VARIABLE lRet AS LOGICAL NO-UNDO.
RUN OpenClipboard(0, OUTPUT iRet).
IF iRet <> 0 THEN
DO:
RUN EmptyClipboard(OUTPUT iRet) NO-ERROR.
SET-SIZE(mRet) = LENGTH(cText,'RAW') + 1.
PUT-STRING(mRet,1) = cText.
RUN SetClipboardData(1, GET-POINTER-VALUE(mRet), OUTPUT iRet).
IF iRet <> 0 THEN lRet = TRUE.
/* SET-SIZE(mRet) = 0.*/
RUN CloseClipboard(OUTPUT iRet) NO-ERROR.
END.
RETURN lRet.
END FUNCTION.
DEFINE VARIABLE cText AS LONGCHAR NO-UNDO.
ASSIGN cText = 'Text'.
SetClipboardText(cText).
MESSAGE "Clipboard set once." VIEW-AS ALERT-BOX.
ASSIGN cText = 'Newt'.
SetClipboardText(cText).
MESSAGE "Clipboard set twice." VIEW-AS ALERT-BOX.