我需要将一个长度超过32 / 64KiB的字符串从我的程序传递到剪贴板,因为OpenEdge中内置的CLIPBOARD函数将其作为限制,我不得不求助于使用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.
答案 0 :(得分:0)
是的,Progress处理剪贴板的方式有一些限制。
在线帮助中有一个注释:
注意:在Windows中,剪贴板最多可以存储64K的数据。
所以是的,有一个限制会迫使你以另一种方式做到这一点。
这很可能与您清空时正在使用的原始变量有关。
如果我删除
SET-SIZE(mRet) = 0.
我可以再次打开剪贴板。
根据知识库中的条目(见下文),我猜测dll已经解除了memptr的分配,因此不需要你这样做(或者更确切地说 - 再次释放会导致崩溃)。所以简单地删除释放应该真正解决它。
在MSDN中读取,您可以看到一旦调用了SetClipboardData,“system”就拥有了指针。因此,一个可行的解决方案必须是每次都创建一个新的指针。将指针存储在一个数组中,并在退出时释放它们。
来自MSDN:
如果SetClipboardData成功,则系统拥有hMem参数标识的对象。一旦所有权转移到系统,应用程序可能不会写入或释放数据,但它可以锁定和读取数据,直到调用CloseClipboard函数。 (在剪贴板关闭之前必须解锁内存。)如果hMem参数标识了一个内存对象,则必须使用带有GMEM_MOVEABLE标志的函数分配该对象。
答案 1 :(得分:0)
在联系Progress Support之后,我得到了一个有效的版本,主要是通过完全绕过正在进行的内置函数。
FUNCTION SetClipboardText RETURNS LOGICAL (cText AS LONGCHAR):
DEFINE VARIABLE iRet AS INT64 NO-UNDO INIT 0.
DEFINE VARIABLE mRet AS MEMPTR NO-UNDO.
DEFINE VARIABLE lRet AS LOGICAL NO-UNDO INIT FALSE.
DEFINE VARIABLE iHnd AS INT64 NO-UNDO INIT 0.
DEFINE VARIABLE iPtr AS INT64 NO-UNDO INIT 0.
/* Open the clipboard for processing */
RUN OpenClipboard(0, OUTPUT iRet).
IF iRet <> 0 THEN
DO:
/* Tell the clipboard to clear itself */
RUN EmptyClipboard(OUTPUT iRet) NO-ERROR.
/* Globally allocate memory for the clipboard data */
RUN GlobalAlloc(2, LENGTH(cText,'RAW') + 1, OUTPUT iHnd).
RUN GlobalLock(iHnd, OUTPUT iPtr).
/* Assign the global memory to the memory pointer */
SET-POINTER-VALUE(mRet) = iPtr.
/* Copy the supplied value to the global memory region */
PUT-STRING(mRet,1) = cText.
/* Unlock the memory so that clipboard can read it */
RUN GlobalUnlock(iHnd, OUTPUT iPtr).
/* Tell the clipboard to copy the data */
RUN SetClipboardData(1, iHnd, OUTPUT iRet).
IF iRet <> 0 THEN lRet = TRUE.
/* Close the clipboard */
RUN CloseClipboard(OUTPUT iRet) NO-ERROR.
/* Free the memory once the clipboard is closed */
IF iHnd <> 0 THEN
RUN GlobalFree(iHnd, OUTPUT iRet).
END.
RETURN lRet.
END FUNCTION.