Буфер обмена для своих объектов

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Alexanbar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1727
Зарегистрирован: 13.04.2004 (Вт) 23:04
Откуда: Волгоградская обл.

Буфер обмена для своих объектов

Сообщение Alexanbar » 09.04.2008 (Ср) 18:56

Есть некий класс Works. Нужно сделать операции с буфером обмена для него.

Делаю так:

Код: Выделить всё
'класс-модуль Works

Public Function CopyToClipboard(Optional ByVal hwnd As Long, _
Optional ByVal SelectedOnly As Boolean = True) As Boolean

Dim wFormat&, hMem&, lpData&
Dim ClpWork As Works


If SelectedOnly Then
    Set ClpWork = New Works
   
    For i = 1 To Count
        If Item(i).Selected Then
            ClpWork.AddNewWork Item(i), True
        End If
    Next i
Else
    Set ClpWork = Me
End If

If ClpWork.Count > 0 Then

    wFormat = RegisterClipboardFormat("Work.lst")
    If wFormat <> 0 Then
        If OpenClipboard(hwnd) <> 0 Then
               
           
            hMem = GlobalAlloc(GHND, ByVal 4&)

            If hMem <> 0 Then
                   
                lpData& = GlobalLock(hMem)
               
                If lpData <> 0 Then
               
                    CopyMemory ByVal lpData, ByVal ObjPtr(ClpWork), ByVal 4&
                    GlobalUnlock hMem
                   
                    EmptyClipboard
                   
                    Call SetClipboardData(wFormat&, hMem&)
                    CopyToClipboard = True
           
                End If
            End If
           
        End If
           
        CloseClipboard
       
    End If
End If
End Function

Public Sub PasteFromClipboard()
Dim wFormat&, hMem&, lpData&
Dim ClpWork As Works

wFormat = RegisterClipboardFormat("Work.lst")
If wFormat <> 0 Then
    If OpenClipboard(hwnd) <> 0 Then
       

        hMem = GetClipboardData(wFormat)
       
        If hMem <> 0 Then
            lpData = GlobalLock(hMem)
           
            If lpData <> 0 Then
               
               
                CopyMemory ClpWork, ByVal lpData, ByVal 4&
               
               
       
                GlobalUnlock hMem
               
                 
                For i = 1 To ClpWork.Count
                    AddNewWork ClpWork(i), True

                Next i
               
               
            End If
           
           
        End If
        CloseClipboard
       
       
    End If
End If
End Sub


Копирование в буфер проходит без видимых ошибок.

Вставка из буфера не работает (вырубается приложение).

Неверно выполняется строка из Sub PasteFromClipboard:

Код: Выделить всё
CopyMemory ClpWork, ByVal lpData, ByVal 4&.

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 10.04.2008 (Чт) 8:42

Нужно корректно устанавливать счетчик ссылок объекта - функции AddRef/Release интерфейса IUnknown.

Или обнулять (но не через Nothing) ClpWork после копирования в буфер.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Alexanbar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1727
Зарегистрирован: 13.04.2004 (Вт) 23:04
Откуда: Волгоградская обл.

Сообщение Alexanbar » 10.04.2008 (Чт) 8:55

Vi писал(а):Нужно корректно устанавливать счетчик ссылок объекта - функции AddRef/Release интерфейса IUnknown.

Или обнулять (но не через Nothing) ClpWork после копирования в буфер.


А подробнее?

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 11.04.2008 (Пт) 10:37

Я не знаю варианта с AddRef/Release, но могу продемонстрировать обнуление.
Код: Выделить всё
Public Function CopyToClipboard ...
...
          CopyMemory ByVal lpData, ClpWork, ByVal 4& ' !!! Сравни со своим вариантом
          Dim lZero As Long
          lZero = 0
          CopyMemory ClpWork, lZero, ByVal 4& ' эквивалент  Set ClpWork = Nothing без уменьшения счетчика ссылок
...
Public Sub PasteFromClipboard...
...
          CopyMemory ClpWork, ByVal lpData, ByVal 4&
...
          EmptyClipboard ' только один раз
или после использования
          Dim lZero As Long
          lZero = 0: CopyMemory ClpWork, lZero, ByVal 4& ' несколько раз
...

В любом случае учти, что скопированный в Clipboard объект не удаляется из памяти, т.к. имеет увеличенную ссылку. И когда-то должен быть удален. Есть также проблема в возможном отсутствии Paste после Copy.

Лучше всего помещать эти скопированные объекты в некий массив/коллекцию, чтобы VB сам их освободил в конце работы.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Alexanbar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1727
Зарегистрирован: 13.04.2004 (Вт) 23:04
Откуда: Волгоградская обл.

Сообщение Alexanbar » 11.04.2008 (Пт) 17:45

Откорректировал обе процедуры.
Код: Выделить всё
Public Function CopyToClipboard(Optional ByVal hwnd As Long, _
Optional ByVal SelectedOnly As Boolean = True) As Boolean

Dim wFormat&, hMem&, lpData&
Dim ClpWork As Works
'Dim lZero As Long

On Error Resume Next

If SelectedOnly Then
    Set ClpWork = New Works
   
    For i = 1 To Count
        If Item(i).Selected Then
            ClpWork.AddNewWork Item(i), True
        End If
    Next i
Else
    Set ClpWork = Me
End If

If ClpWork.Count > 0 Then

    wFormat = RegisterClipboardFormat("Work.lst")
    If wFormat <> 0 Then
        If OpenClipboard(hwnd) <> 0 Then
               
           
            hMem = GlobalAlloc(GHND, ByVal 4&)

            If hMem <> 0 Then
                lpData& = GlobalLock(hMem)
               
                If lpData <> 0 Then
               
                   
                    CopyMemory ByVal lpData, ClpWork, ByVal 4&
                   
                   
                   
                    CopyMemory ClpWork, 0&, 4&
                   
                    GlobalUnlock hMem
                   
                   
                   
                    EmptyClipboard
                   
                    Call SetClipboardData(wFormat&, hMem&)
                    CopyToClipboard = True
           
                End If
            End If
           
        End If
           
        CloseClipboard
       
    End If
End If
End Function
Public Sub PasteFromClipboard(Optional ByVal hwnd As Long)
Dim wFormat&, hMem&, lpData&
Dim ClpWork As Works



wFormat = RegisterClipboardFormat("Work.lst")
If wFormat <> 0 Then
    If OpenClipboard(hwnd) <> 0 Then
       
       
        hMem = GetClipboardData(wFormat)
       
        If hMem <> 0 Then
            lpData = GlobalLock(hMem)
           
         
            If lpData <> 0 Then
               
                CopyMemory ClpWork, ByVal lpData, ByVal 4&
               
               
                 
               
               
                On Error Resume Next
                If Not ClpWork Is Nothing Then
                   
                    For i = 1 To ClpWork.Count
                       
                        ClpWork(i).Selected = False
                        AddNewWork ClpWork(i), True
   
                    Next i
                   
               
                End If
                Err.Clear
                On Error GoTo 0
               
               
                CopyMemory ClpWork, 0&, 4&
               
               
               
            End If
     
            GlobalUnlock hMem
           
        End If
        CloseClipboard
       
    End If
End If
End Sub




Работает только при условии, если обмен идёт между двумя копиями приложения.

Пробовал запускать исходное приложение и переименованное.

ПРи копировании с помощью исходного и попытки вставить в буфер перименованного идёт вырубание.

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 14.04.2008 (Пн) 8:57

А что ты хочешь, передавая какой-то внутренний адрес приложения? Чтобы передать объект (как адрес объекта) нужно задействовать СОМ систему, иначе кто будет маршаллировать твой интерфейс в чужой процесс?

Есть хороший моникер, создаваемый через CreateObjrefMoniker. Он имеет строковое представление, которое даст объект через VB-шный GetObject.

Поищи здесь на форуме, вроде, tyomitch тоже решал эту проблему.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Alexanbar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1727
Зарегистрирован: 13.04.2004 (Вт) 23:04
Откуда: Волгоградская обл.

Сообщение Alexanbar » 15.04.2008 (Вт) 19:13

А что ты хочешь, передавая какой-то внутренний адрес приложения?


Выходит, про то, что написано в MSDN, нужно забыть? Обидно.

Есть хороший моникер, создаваемый через CreateObjrefMoniker


Не нашёл, к сожалению. В строку я могу преобразовать свой объект и самостоятельно, поскольку он берётся из текстового файла, и туда же отправляется при обновлении.

Но строка тоже копируется в буфер только в пределах приложения.

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 16.04.2008 (Ср) 9:47

Но строка тоже копируется в буфер только в пределах приложения.
Если ты применяешь эту же технику, то - да. Но для строк есть другие форматы Clipboard, которые могут переносить строки между приложениями. Есть и стандартный объект Clipboard, работа с которым прозрачна и надежна.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Alexanbar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1727
Зарегистрирован: 13.04.2004 (Вт) 23:04
Откуда: Волгоградская обл.

Сообщение Alexanbar » 16.04.2008 (Ср) 13:29

Vi писал(а):Но для строк есть другие форматы Clipboard, которые могут переносить строки между приложениями. Есть и стандартный объект Clipboard, работа с которым прозрачна и надежна.


Дело в том, что строка, соответсвующая моему объекту, не предназначена для вставки в блокнот или, скажем, в MS Word.
Верно и обратное. Не каждая текстовая строка является текстовым представлением моего объекта.

Но, похоже, придётся добавлять в начало строки некую сигнатуру и работать со стандартным объектом Clipboard и форматом VBCFText, хотя это и неудобно.

К сожалению, метод SetText отказывается посылать строку для форматов, которые не являются встроенными.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 16.04.2008 (Ср) 14:13

А почему бы не сериализовать PropertyBag?
Lasciate ogni speranza, voi ch'entrate.

Alexanbar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1727
Зарегистрирован: 13.04.2004 (Вт) 23:04
Откуда: Волгоградская обл.

Сообщение Alexanbar » 16.04.2008 (Ср) 15:43

alibek писал(а):А почему бы не сериализовать PropertyBag?


Как это связано с буфером обмена?

Alexanbar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1727
Зарегистрирован: 13.04.2004 (Вт) 23:04
Откуда: Волгоградская обл.

Сообщение Alexanbar » 21.04.2008 (Пн) 10:13

Проблема решена.
Суть решения.

1) Объект представлен в виде байтового массива btW.

2) Выделяется память соответсвующего размера
Код: Выделить всё

lx&=Ubound(btW)+1
hMem = GlobalAlloc(GHND, ByVal lx)


3) При использовании CopyMemory указывается размер массива

Код: Выделить всё
CopyMemory ByVal lpData, btW(0), ByVal lx


4) Операторов типа CopyMemory ClpWork, 0&, 4& не требуется


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 92

    TopList