DragDrop Menu problem

0

Zabrałem się za implementację ole-drag-drop dla menuitems (ala menu start)
Wszystko już działa oprócz eventu drop - program dostaje kopa a błąd jest gdzieś w instancji ole 0x12F3FE (na 100% to nie jest moja instancja)

Zaimplementowałem IDataObject, IDropSource, IDropTarget, IEnumFORMATETC, IExternalConnection, IStdMarshalInfo i chwilowo IMarshal, ale z nim program się zapętlał: ciągle tworzył nową instancję IDataObject albo dodawał do niej referencję

Brakuje mi tylko implementacji CoClass IdentityUnmarshal i tajemniczego "4C1E39E1-E3E3-4296-AA86-EC938D896E92"
Google nie potrafi pomóc bo nikt nie wie jak otrzymać notify WM_MENUGETOBJECT
Ja dostaję tą notyfikację i zwracam instancję mojego IDropTarget

Inny problem: RegisterDragDrop() blokuje mi IDataObject - gdy zamykam program - ta klasa ma dwie dodatkowe referencje. Oczywiście najpierw robię RevokeDragDrop i OleUninitialize(), ale potem w pętli muszę zrobić:

while (pDataObject->Release())

żeby zwolnić pamięć

Może ktoś to robił i mi podpowie co może być źle

małe demko: przeciągnij jakiegoś itema z menu na okno - nic się nie stanie
przeciągnij na menu - bum!
http://free.of.pl/s/sapero/dragdrop_menu_all.zip
w dodatkowym oknie konsoli widać jakie metody są wywoływanie i adres skąd zostały wywołane

0

A próbowałeś zainteresować sie zestawem

SetMenuInfo
MNS_DRAGDROP
WM_MENUDRAG
WM_MENUGETOBJECT

Co do interfejsów to wydaje mi się, że
IDataObject
IDropSource
IDropTarget
są wystarczające (ew. IEnumFORMATETC jak się używa)

jestem ciekaw rezultatów !

0
sapero napisał(a)

Inny problem: RegisterDragDrop() blokuje mi IDataObject - gdy zamykam program - ta klasa ma dwie dodatkowe referencje. Oczywiście najpierw robię RevokeDragDrop i OleUninitialize(), ale potem w pętli muszę zrobić:

while (pDataObject->Release())

Tak być nie powinno. Policz dokładnie referencje w swoim kodzie. Jeżeli coś zostanie, a w twoim programie wszystko jest OK, nie musisz (a raczej nie możesz) zwalniać "do zera" obiektu IDataObject.

0

reichel, to wszystko już mam za sobą, menu ma ustawioną flagę MNS_DRAGDROP, Ole jest zainicjowane...
Zaraz gdy system wyczai że chcę coś przesunąć w menu:

  1. dostaję WM_MENUDRAG
  2. tworzę dwie klasy IDropSource i IDataObject
  3. ustawiam dane: pDataObject->SetData()
    gdzie FORMATETC:
 .cfFormat = CF_HDROP
 .ptd      = NULL
 .dwAspect = DVASPECT_CONTENT
 .lindex   = -1
 .tymed    = TYMED_HGLOBAL

STGMEDIUM: .tymed = TYMED_HGLOBAL, .pUnkForRelease = NULL a w tej pamięci (.hGlobal) jest zaalokowana struktura DROPFILES z plikiem o nazwie

wsprintf(*pDrop.str, "droptype='menu';hwnd='%d';hMenu='%d';item='%d';", hwnd, hMenu, item)

co łatwo rozbić na linked-list typu string=string; DictLookup(mydict, "hwnd") zwraca hwnd w postaci stringa...

potem mam hr=DoDragDrop(pDataObject, pDropSource, wsje_flagi, &retu)
Posuwam jakimś itemem z menu i jak go upuszczę to odbieram WM_MENUGETOBJECT gdzie lParam = LPMENUGETOBJECTINFO

if IsEqualGuid(*lParam.*riid, _IID_IDropTarget){
  *lParam.pvObj = pIDropTarget);
  return MNGO_NOERROR;} // chcę być też klientem!

IDataObject, IDropSource i IDropTarget - tyle wystarczy żeby zadziałało, już na samym początku to sprawdzałem, ale po kilku kopach zacząłem dodawać wszystkie inne o które pytało OLE w DataObject::QueryInterface

Co ciekawe - upuszczając itema na pasku zadań (taskliste,tray) - DoDragDrop zwraca DRAGDROP_S_DROP i nie ma exceptiona, ale system się ciepie że tu nie wolno upuszczać

... while release() - wiem że to źle, ale chociaż zwolnię te kilka bajtów zanim program się skończy; i raczej usunę RegisterDragDrop() żeby temu zapobiec

Nabyłem cynk że na vbaction.net było coś o tym ale serwer się skończył [green]

0

[...] ale po kilku kopach zacząłem dodawać wszystkie inne o które pytało OLE w DataObject::QueryInterface

Jeżeli mechanizm drag&drop w menu jest podobny do tego "zwykłego" to wystarczą te interface'y, które podał reichel.

... while release() - wiem że to źle, ale chociaż zwolnię te kilka bajtów zanim program się skończy;

Z takim podejściem to zawsze jakieś błędy będą ;)

0

Już prawie gotowe tylko że przesuwanie itema nie działa u mnie tak jak powinno :/
W powyższym samplu - zwykłe itemy chyba zawsze da się przenosić, ale z popupem są jaja [sciana]

  1. ciągnę go na pierwszą pozycję : ok
  2. następnie ciągnę go w dół - blah, zły parametr, GetMenuItemInfo nie ustawia (conajmniej) menuiteminfo.fType

exceptiona pozbyłem się zwracając DRAGDROP_S_CANCEL (zawsze!) w CDropSource::QueryContinueDrag, ale w tej metodzie dodałem kod który przesuwa itema.
Powróciłem do implementacji tylko trzech klas. Szkoda zachodu o nic.

aha, "celownik" drop-a niekiedy znika a niekiedy pozostaje po drop'ie [rotfl]
user image

0

To

exceptiona pozbyłem się zwracając DRAGDROP_S_CANCEL (zawsze!) w CDropSource::QueryContinueDrag, ale w tej metodzie dodałem kod który przesuwa itema.

jest dość dziwne ?!

Nie można wrocić do podmenu

A próbowałeś kiedyś zaimplementować dla popupmenu ? Mi coś nie działało już na etapie SetmenuInfo, niby OK ale nie dostaje komunikatów.

0

z popupem (contextmenu) też działa, zauważyłem że dodanie MIM_APPLYTOSUBMENUS powoduje że menu nie znika po drop'ie, ale problem z submenu pozostaje

Spróbuj tak

MENUINFO mi
mi.cbSize  = sizeof(MENUINFO)
mi.fMask   = MIM_STYLE | MIM_APPLYTOSUBMENUS
GetMenuInfo(hMenu, &mi)
mi.dwStyle |= MNS_DRAGDROP
SetMenuInfo(hMenu, &mi)

w subclasie okna mam

case WM_MENUDRAG
   pDropTarget->setSource(hwnd, lParam, wParam) // dodatkowa metoda
   hr = DoDragDrop(pDataObject, pDropSource, DROPEFFECT_MOVE, &dwEffect)
   return MND_CONTINUE

case WM_MENUGETOBJECT
   settype lParam, MENUGETOBJECTINFO
   settype *lParam.riid, GUID
   pDropTarget->setDestination(*lParam.hmenu, *lParam.uPos) // dodatkowa metoda
   *lParam.pvObj = pDropTarget
   return MNGO_NOERROR

do konstruktora CDropSource dodałem pointery do pozostałych dwóch klas, więc w razie drop'a mam tak:

// CDropSource::QueryContinueDrag
INT   temp

if fEscapePressed then return DRAGDROP_S_CANCEL

if (grfKeyState & VK_LBUTTON) = 0
	this.pDropTarget->Drop(this.pDataObject, grfKeyState, &pt, &temp)
	return DRAGDROP_S_CANCEL
endif

return S_OK

czyli wcale nie potrzebuję CDataObject, ale OLE go wymaga więc jest

w target :

// CDropTarget::Drop
  MoveMenuItem(this.s_hwnd, this.s_hMenu, this.s_item, this.d_hMenu, this.d_item)
  return S_OK

MoveMenuItem() to możesz sobie podejżeć, nie wiem co tam jest źle że submenu tylko raz pozwala się przesunąć. Brakuje jeszcze MenuItemFromPoint() w razie dropa poza menu

Znalazłem coś znowu - IShellMenu, troszkę to poskubałem ale nie udało mi się dodać ani jednej pozycji do tego menu. Wszystkie metody zwracają S_OK a google milczy

0

No coż ja tak robie - ale context menu jak nie działa tak nie działa ?

walka z powłoką windows trwa dalej :)

Dopisane 17.10.2005

Taaa istotnie IShellMenu nie należy do dobrze opisanych :)

co ciekawe po SetMenu w callbacku dostaje uMsg 0x2b a takiego nie ma (w MSDN)

1 użytkowników online, w tym zalogowanych: 0, gości: 1