AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

AHK DDE FUNCTIONS. (Including a/sync,batch,multi-channel)

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 417
Location: Galil, Israel

PostPosted: Thu Aug 09, 2007 5:46 am    Post subject: AHK DDE FUNCTIONS. (Including a/sync,batch,multi-channel) Reply with quote

AHK should (with this) now have full DDE CLIENT FUNCTIONALITY. In fact, MORE FUNCTIONALITY than the windows DDE DLL (you are not limited to one conversation per topic per server.., full async and batch support).


1. Why would you want this power ??


check out these:


http://www.autohotkey.net/~Joy2DWorld/DDE/WRDBASIC.HLP

http://www.autohotkey.net/~Joy2DWorld/DDE/XLMACR8.HLP



2. Special Notes:


have been trying to get this done for A LONG LONG TIME...

see:

http://www.autohotkey.com/forum/topic14740.html&highlight=dde

and

http://www.autohotkey.com/forum/topic17326.html&highlight=dde




my own brain does not seem to like the philosphical approach taken by MS.. and so... meeting the MS requirements.. (getting things to work in windows.., is *not* my strongest side...)


this was only possible thanks to Sean who filled in with the crucial and necessary exactness, the missing pieces of the puzzle... (like mis-defining memory as locked vs. moveable).


HUGE CREDIT for Sean's help.

THANKS!!!




The DDE is wrapper actually not that complex, or hard to code up, but was haunted by all kinds of AHK issues... some of which (with the right opions set) still haunt the code....




This is not ready yet... not cleaned up enough... not with enough examples or instructions....


but I post it now, and will come back and clean up later.

(hope that's ok).



is WORKING and TESTED....

AMAZING STUFF with WORD and EXCELL...


SUPER SUPER EASY TO USE, and you have full control-->

exactly like using the old WORD BASIC....


just send a DDE command to do something... and it does it....


note it uses windows (above gui 20), for the connections.
DDE needs this.
each NEW connection can be in the *same* gui winow,
*EXCEPT* if you want multi-threaded conversations to SAME SERVER,
with same topic. To do that needs more gui windows... and so that is what we do... ..



HOW TO USE IT ?:


Quote:

Step 1: Connect to a DDE Server.

DDE_connection_Number1 := DDE_Connect("NAME_OF_DDE_SERVER","TOPIC_REQUESTED", OPTIONAL_CONNECTION_NUMBER_1_TO_20, OPTIONAL_HANDLE_OF_DDE_SERVER_WINDOW )


if you have more than one connection, you use the "DDE_connection_Number1", "DDE_connection_Number2" var to designate in Requests, Pokes, etc..


You can have up to 20 connections WITH THE SAME SERVER ON THE SAME TOPIC.

You do not need a new connection# for different Servers or Different topics!!!!




Step 2: Send a command:

result := DDE_Execute("[COMMAND YOU WANT TO SEND]", OPTIONAL_CONNECTION_ID_EG_DDE_connection_Number, OPTIONAL_DELAY_NUMBER)

note: you do not need to pay attention or use connection ID's unless you have multiple connections. THE LAST ACCESSED CONNECTION IS THE DEFAULT, SO DOES NOT NEED TO BE REDESIGNATED, UNLESS YOU WANT TO CHANGE IT.


also can:

result := DDE_Request("[COMMAND]", OPTIONAL_connection_ID, OPTIONAL_DELAY)
usually_not_one := DDE_Poke("[COMMAND]", "[DATASET FOR COMMAND]", OPTIONAL_connection_ID, OPTIONAL_DELAY)




Step 3: (when you're all done!) Terminate Connection

DDE_KILL("SERVER_NAME","TOPIC", OPTIONAL_CONNECTION_NUMBER)

DDE_KILL() = kills all open connectinos.



Super-Powers: (in additional to multiple connections to same server&topic)

1. You don't need to track open servers & topics, all done for you.
DDE_Connect(Server,Topic) to existing open topic on that server returns you the Conversation_ID contained in the "DDE_connection_Number1" variable in example above. ie. Connection_X := DDE_Connect(Server,Topic) fills Connection_X with the conversation ID for taht conversation. Can have multiple conversationID's for same topic/server if made on different DDE connection ports: eg. DDE_Connect(Server,Topic,2), etc.


2. BATCH MODE!

uses following Global Bars:

__DDE_Answer_Table = holds your batch responses. (You can clear & modify it.. but best to do it in a CRITICAL segment!! so not get accidental conflict with new data recepiet)

__DDE_BatchMode = Set this to use batch mode!!

if set, you do not get Answer := Request('[xxxx') instead answer goes only to answer table. IE NO DELAY WAITING FOR ANSWER!!!!!!

__DDE_Use_Answer_Table = you DO get an answer, and you ALSO build an answer table

__DDE_SafeMode = for multiple connection used at same time, etc. if set TRACKS MESSAGE INCOMING DATA AGAINST INTENDED CONNECTION and SERVER. This is **IMPORTANT** where you have data comming in from multiple servers and/or connections in NON-BATCH mode.

it makes sure that the answer you get actually comes from the CONNECTION and the SERVER you are expecting it to be from.


usage note: [and this is well beyond basic use.. if you want to control word, etc. likely this is not something you even need to read] __DDE_Use_Answer_Table and __DDE_SafeMode should be normal operation mode for multiple active servers & connections with much async data exchange.

if you seek specific response to command X... all other's will be either qued, or saved in the answer table....





3. Debug AHK stuff:

__DDE_UseAHKMessages set to use AHK sendmessage in place of DLL,

will cause crash (eventually)....

option to allow debugging... or

for those bored with too much stability....




EXSample

Code:


;  this is just some test data used ... left as examples...

 msgbox % DDE_Connect("WINWORD","System")
 DDE_execute("[InsertField .Field = ""text""][insert ""testttt""][Insert  ""this is also a execute test {enter}""]{enter}{enter}")
 DDE_execute("[ViewZoom .ZoomPercent = 127][Insert ""Where was this added ?? NEW!""] {Enter} {Enter}")
DDE_Execute("[AppActivate ""Document1""]")
msgbox %  DDE_Request("Topics") "`n" DDE_Request("System")
msgbox % DDE_Request("Formats")

DDE_Execute("[InsertFieldChars][Insert ""set ZZZ """"This is cool""""""]")
DDE_Execute("[CharRight][CharRight]")
DDE_Execute("[Insert chr$(13)]")

DDE_Execute("[InsertFieldChars]")

DDE_Execute("[Insert ""set BBB """"This is cool""""""]")

msgbox

DDE_exitapp() ; to close all open DDE transactions





Actual DDE LIBRARY FUNCTION CODE, ie. COPY THIS AND SAVE IT AS "DDE.AHK" IN YOUR AHK 'LIB' DIRECTORY:


Code:



/* ======================================================


Step 1:  Connect to a DDE Server.

DDE_connection_Number1 := DDE_Connect("NAME_OF_DDE_SERVER","TOPIC_REQUESTED", OPTIONAL_CONNECTION_NUMBER_1_TO_20, OPTIONAL_HANDLE_OF_DDE_SERVER_WINDOW )


if you have more than one connection,  you use the "DDE_connection_Number1", "DDE_connection_Number2"  var to designate in Requests, Pokes, etc..


You can have up to 20 connections WITH THE SAME SERVER ON THE SAME TOPIC.

You do not need a new connection# for different Servers or Different topics!!!!




Step 2:  Send a command:

result := DDE_Execute("[COMMAND YOU WANT TO SEND]", OPTIONAL_CONNECTION_ID_EG_DDE_connection_Number, OPTIONAL_DELAY_NUMBER)

note:   you do not need to pay attention or use connection ID's unless you have multiple connections.  THE LAST ACCESSED CONNECTION IS THE DEFAULT,  SO DOES NOT NEED TO BE REDESIGNATED, UNLESS YOU WANT TO CHANGE IT.


also can:

result := DDE_Request("[COMMAND]", OPTIONAL_connection_ID, OPTIONAL_DELAY)
usually_not_one := DDE_Poke("[COMMAND]", "[DATASET FOR COMMAND]",  OPTIONAL_connection_ID, OPTIONAL_DELAY)




Step 3: (when you're all done!)   Terminate Connection

DDE_KILL("SERVER_NAME","TOPIC", OPTIONAL_CONNECTION_NUMBER)

DDE_KILL() = kills all open connectinos.



Super-Powers:  (in additional to multiple connections to same server&topic)

1. You don't need to track open servers & topics,  all done for you.
DDE_Connect(Server,Topic) to existing open topic on that server returns you the Conversation_ID  contained in the "DDE_connection_Number1"  variable in example above.  ie.  Connection_X := DDE_Connect(Server,Topic)  fills Connection_X with the conversation ID for taht conversation.   Can have multiple conversationID's for same topic/server if made on different DDE connection ports:  eg.  DDE_Connect(Server,Topic,2), etc.


2.  BATCH MODE!

uses following Global Bars:

__DDE_Answer_Table  = holds your batch responses.  (You can clear & modify it.. but best to do it in a CRITICAL segment!!  so  not get accidental conflict with new data recepiet)

__DDE_BatchMode = Set this to use batch mode!!

if set,  you do not get Answer := Request('[xxxx')  instead answer goes only to answer table.  IE NO DELAY WAITING FOR ANSWER!!!!!!

__DDE_Use_Answer_Table  =  you DO get an answer, and you ALSO build an answer table

__DDE_SafeMode = for multiple connection  used at same time, etc.  if set TRACKS MESSAGE INCOMING DATA AGAINST INTENDED CONNECTION and SERVER.   This is **IMPORTANT** where you have data comming in from multiple servers and/or connections in NON-BATCH mode.

it makes sure that the answer you get actually comes from the CONNECTION and the SERVER you are expecting it to be from.


usage note:  [and this is well beyond basic use.. if you want to control word, etc.  likely this is not something you even need to read]   __DDE_Use_Answer_Table  and __DDE_SafeMode  should be normal operation mode for multiple active servers & connections with much async data exchange.

if you seek specific response to command X...  all other's will be either qued,  or saved in the answer table....





3. Debug AHK stuff:

__DDE_UseAHKMessages  set to use AHK sendmessage in place of DLL,

will cause crash (eventually)....

option to allow debugging... or

for those bored with too much stability....



*/   ;======================================================

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  HERE STARTS THE ACTUAL DDE WRAPPER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;------------------  DDE CLIENT WRAPPER ---------------------------------------

DDE_Connect(DDE_Server,DDE_Topic = "", DDEConnection_ = 0, DDE_Init_To = 0xFFFF )  {
      ; DDE_Init_To = 0 to test to see if connection exists & activate as default
   global
   
   static  DDE_Assigned_Messages
   
   
   local nDDE_Topic, nAppli, Conversation, hAH , sDDE_Topic,sDDE_Server ; , hAHK

   DDE_Connection_ReNumber(DDEConnection_)
   
   if (DDE_Topic = "")
      DDE_Topic := DDE_Server   
      
   if InStr(DDE_Topic . DDE_Server, "\E" , 1) {
      sDDE_Topic := regexreplace(DDE_Topic,"\\E","\E\\E\Q")
      sDDE_Server := regexreplace(DDE_Server,"\\E","\E\\E\Q")
   } else {
      sDDE_Topic = %DDE_Topic%
      sDDE_Server = %DDE_Server%
   }
   
   DDE_Topic_List_Matchme :=
            
   if regexmatch( __DDEM_DDE_Topic_List
             ,  "\n\Q"
            . sDDE_Topic
            . "|"
            . sDDE_Server
            .  "|"
            . DDEConnection_
            . "|\E"
            . "(?<DDE_Server_ID>[^|]++)"
            . "\|"
            .  "(?<Client_ID>[^`n]++)", __DDEM_ )  {
            
         __Conversation_BASE := DDEConnection_
                     . "|"
                     .  DDE_HEX(__DDEM_DDE_Server_ID)
                     . "|"
                     . DDE_HEX(__DDEM_Client_ID)
                     
         return __Conversation_BASE
      
   } else if !DDE_Init_To
      return


   if __DDE_UseAHKMessages
      DetectHiddenWindows, On 

   if !DDE_Assigned_Messages {
      CF_TEXT := 1
      WM_DDE_INITIATE   := 0x3E0
      WM_DDE_TERMINATE:= 0x3E1
      WM_DDE_ADVISE   := 0x3E2
      WM_DDE_UNADVISE   := 0x3E3
      WM_DDE_ACK   := 0x3E4
      WM_DDE_DATA   := 0x3E5
      WM_DDE_REQUEST   := 0x3E6
      WM_DDE_POKE   := 0x3E7
      WM_DDE_EXECUTE   := 0x3E8   
      
      __DDE_TableDiv :=  "`n$[_DDE_TABLE_DIV_]$"
      __DDE_NULL :=  "$[_DDE_NULL_]$"
      
      OnMessage(WM_DDE_ACK , "DDE_ACK")
      OnMessage(WM_DDE_DATA, "DDE_DATA")
      DDE_Assigned_Messages = 1
      
   }


   if !regexmatch(__DDEConnection_Table , "x)\n\Q"
                        . DDEConnection_
                        . "|\E"
                        . "(?<K>[^`n]++)", hAH )
   {
      Gui, %DDEConnection_%: +LastFound
      Gui, %DDEConnection_%: Show, hide w1 h1 ,__DDEConnection_%DDEConnection_%
      hAHK := WinExist()
      __DDEConnection_Table .= "`n" . DDEConnection_ . "|" . DDE_HEX(hAHK)  ; msgbox %
   }     
   
   Ack_W =

   nAppli := DllCall("GlobalAddAtom", "str", DDE_Server, "Ushort")
   nDDE_Topic := DllCall("GlobalAddAtom", "str", DDE_Topic, "Ushort")

   initWait := 120000
   
   loop 10 {
      sleep 500
      if !__DDE_UseAHKMessages
         DllCall("SendMessage", "UInt", DDE_Init_To, "UInt", WM_DDE_INITIATE , "UInt", hAHK , "UInt", nAppli | nDDE_Topic << 16)       
      else
         SendMessage, WM_DDE_INITIATE, hAHK, nAppli | nDDE_Topic << 16,, ahk_id %DDE_Init_To%
      ;;
      if (__DDEM_DDE_Server_ID := DDE_WaitA(hAHK, initWait))
         break
   }
   
   DllCall("DeleteAtom", "Ushort", nAppli)
   DllCall("DeleteAtom", "Ushort", nDDE_Topic)

   if !(__DDEM_DDE_Server_ID)
      return ( DDE__Tooltips("  ++DDE INITIALIZATION ERROR ".  DDE_Topic . hAHK . "++ " , 6000, 4) * 0)

   if !(__DDEM_Client_ID := Ack_Hwnd)  {  ; hAHK ; Ack_Hwnd
      __DDEM_Client_ID := DDE_HEX(hAHK)
      DDE__Tooltips("++DDE CLIENT HANDLE INITIALIZATION ERROR ". hAHK . " ++" , 3000, 3)

   }
   
   Conversation :=DDEConnection_ 
            . "|"
            . DDE_HEX(__DDEM_DDE_Server_ID)
            . "|"
            . DDE_HEX(__DDEM_Client_ID)
   
   __DDEM_DDE_Topic_List .= "`n"              
               . DDE_Topic
               . "|"
               . DDE_Server
               . "|"
               . Conversation
   
   Return (__Conversation_BASE := Conversation )
   
}
; :::::::::::::::::::::::::::::::::::::::::::::::::::::::::
   
DDE_Select( byref Conversation) {

   global   
         
   if ( Conversation or (Conversation := __Conversation_BASE) ) and    Regexmatch(Conversation, "^(?<Connection>[^|]++)\|(?<__DDE_Server_ID>[^|]++)\|(?<Client_ID>.++)$", __DDEM_)
      return 1
      
}

DDE_Connection_ReNumber(byref DDEConnection) {

   if !DDEConnection or (DDEConnection < 1) or (DDEConnection > 21)
      DDEConnection := 20
   else
      DDEConnection += 20
      
}



DDE_Wait(byRef Var, max = 120000) {

   loop  %max% {
      if (Var <> "")
         break
      AX++
   }
   if ( AX < MAX  )  ; ie. loop count
      return Var  ; A
}

DDE_WaitS(byRef VarKey, KeyValue, max = 120000) {

   loop  %max% {
      if (VarKey = KeyValue)
         break
      AX++
   }
   
   if ( AX < MAX  )  ; ie. loop count
      return 1
}


DDE_WaitA(Var, max = 60000) {
   global Ack_Hwnd,Ack_W
   ; max =60000
   loop  %max% {  ; 1200000 {
      if  (Var = Ack_Hwnd)
         break
      AX++
   }
   if ( AX < MAX  )  ; ie. loop count
      return Ack_W
}

DDE_Request(Item, Conversation = "", delay = 90000) {

   local hItem
   
   if !DDE_Select( Conversation)
      return DDE__Tooltips("  DDE UNDEFINED DDE REQUEST ERROR ". Conversation . " ** "  , 6000, 3)
   
   hItem := DllCall("GlobalAddAtom", "str", Item, "Ushort")   
   DDE__myAnswer =
   DDE__myAnswerKey =
   DDE__Data_Title =

   if !__DDE_UseAHKMessages
      DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_REQUEST , "UInt", __DDEM_Client_ID , "UInt", CF_TEXT | hItem << 16)
   else
      PostMessage, WM_DDE_REQUEST, __DDEM_Client_ID, CF_TEXT | hItem << 16,, ahk_id %__DDEM___DDE_Server_ID%
   DllCall("DeleteAtom", "Ushort", hItem)   
   
   if __DDE_BatchMode  ; ie. no waiting... just  >>  __DDE_Use_Answer_Table
      return
      
   If DDE_SafeMode
      return DDE_WaitS(DDE__myAnswerKey, __DDEM_Client_ID . "|" . __DDEM___DDE_Server_ID, delay) ? DDE__myAnswer : ""
   else if  (DDE_Wait(DDE__myAnswer, delay) = "$[_~~::NULL::~~_]$")
      return
      
   return DDE__myAnswer
}



DDE_Poke(item= "", data = "", Conversation = "", delay = 20000 ) { 

   Global 
   
   local hItem,hData,pData, lParam
   
   if !DDE_Select( Conversation)
      return DDE__Tooltips("  DDE UNDEFINED DDE POKE  ERROR ". Conversation . " ** "  , 6000, 3)

   
   DDE__myAnswer =
   Ack_Hwnd =
    DDE__Data_Title =
   
   If SubStr(Data, -1) <> "`r`n"
         Data .= "`r`n"

      hItem := DllCall("GlobalAddAtom", "str", Item, "Ushort")
      hData := DllCall("GlobalAlloc", "Uint", 0x0002, "Uint", 2+2+StrLen(Data)+1)   
      pData := DllCall("GlobalLock" , "Uint", hData)
      
      
      DllCall("ntdll\RtlFillMemoryUlong", "Uint", pData, "Uint", 4, "Uint", 1<<13|1<<16)
      DllCall("lstrcpy", "Uint", pData+4, "Uint",&Data)
      DllCall("GlobalUnlock", "Uint", hData)
      
      
      lParam := DllCall("PackDDElParam", "Uint", WM_DDE_POKE, "Uint", hData, "Uint", hItem)
      
   if !__DDE_UseAHKMessages
      DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_POKE , "UInt", __DDEM_Client_ID , "UInt", lParam)
   else
      PostMessage, WM_DDE_POKE, __DDEM_Client_ID, lParam,, ahk_id %__DDEM___DDE_Server_ID%

     If ErrorLevel
      {
         DllCall("DeleteAtom", "Ushort", hItem)
      ;   DllCall("GlobalFree", "Uint"  , hData)
      ;   DllCall("FreeDDElParam", "Uint", WM_DDE_POKE, "Uint", lParam)
      }
   

   return DDE_WaitA(__DDEM_Client_ID, delay)

}





DDE_Execute(item, Conversation = "", delay = 60000) {

   Global 
   
   local hCmd, pCmd
   
   ; WM_DDE_EXECUTE,__DDEM_Client_ID,__DDEM___DDE_Server_ID, DDE__myAnswer, Ack_Hwnd
   
   
   if !DDE_Select( Conversation)
      return DDE__Tooltips("  UNDEFINED DDE EXECUTE  ERROR ". Conversation . " ** "  , 6000, 3)

   
      hCmd := DllCall("GlobalAlloc", "Uint", 0x0002, "Uint", StrLen(item)+1)
      pCmd := DllCall("GlobalLock" , "Uint", hCmd)
      DllCall("lstrcpy", "Uint", pCmd, "str",item)
      DllCall("GlobalUnlock", "Uint", hCmd)
      
   DDE__myAnswer =
   Ack_Hwnd =
    DDE__Data_Title =
   
   if !__DDE_UseAHKMessages
      DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_EXECUTE , "UInt", __DDEM_Client_ID , "UInt", hCmd)
   else
      PostMessage, WM_DDE_EXECUTE, __DDEM_Client_ID, hCmd,, ahk_id %__DDEM___DDE_Server_ID%
   ;If ErrorLevel
       ;  DllCall("GlobalFree", "Uint", hCmd)
      
   return DDE_WaitA(__DDEM_Client_ID, delay )
      
}


DDE_ACK(wParam, LParam, MsgID, hWnd) {
   Critical 24
   global Ack_W,Ack_L,Ack_Hwnd,Ack_MsgID
   
   Ack_W := wParam
   Ack_L := LParam
   Ack_MsgID := MsgID
   Ack_Hwnd := hWnd

}


DDE_DATA(wParam, lParam, MsgID, hWnd) {

   Critical 250
   
   Global WM_DDE_ACK, DDE__myAnswer , DDE__myAnswerKey, DDE_SafeMode, Data_MsgID, DDE__Data_Title , __DDE_NULL, __DDE__myAnswer_Table, __DDE_TableDiv,Use_Answer_Table,  __DDE_UseAHKMessages ; , sInfo
   
               
   nITem = "Defined!"
   
   DllCall("UnpackDDElParam", "Uint", MsgID, "Uint", lParam, "UintP", hData, "UINTP", nItem)
   DllCall(  "FreeDDElParam", "Uint", MsgID, "Uint", lParam)

   pData := DllCall("GlobalLock", "Uint", hData)
   VarSetCapacity(sInfo, DllCall("lstrlen", "Uint", pData+4))
   DllCall("lstrcpy", "str", sInfo, "Uint", pData+4)
   DllCall("GlobalUnlock", "Uint", hData)
   If (*(pData+1) & 0x20)
   DllCall("GlobalFree", "Uint", hData)
   If (*(pData+1) & 0x80) {
      if !__DDE_UseAHKMessages
         DllCall("PostMessage", "UInt", wParam, "UInt", WM_DDE_ACK , "UInt",  hWnd , "UInt", 0x80 << 8 | nItem << 16)
      else {
         DetectHiddenWindows, On 
         PostMessage, WM_DDE_ACK, hWnd, 0x80 << 8 | nItem << 16,, ahk_id %wParam%
      }
   }
   Data_MsgID := MsgID
   DDE__Data_Title := "Undefined!"
   
   DllCall("GlobalGetAtomName", "UintP", nItem, "STR", DDE__Data_Title, "SHORT", 512)


   DDE__myAnswer := sInfo
   if (DDE__myAnswer = "") and !DDE_SafeMode
      DDE__myAnswer := __DDE_NULL
   DDE__myAnswerKey := DDE_HEX(hWnd) . "|" . DDE_HEX(wParam) 
   
   if ( __DDE_BatchMode or __DDE_Use_Answer_Table )
      __DDE__myAnswer_Table .= __DDE_TableDiv
               . DDE__myAnswerKey
               . "|"
               .  DDE__Data_Title
               .  "|"
               . DDE__myAnswer
               
   return 0

}


DDE_KILL(__DDE_Server="",__DDE_Topic = "", __DDE_DDEConnection_ = 0) {

   local s__DDE_Topic,s__DDE_Server

   DDE_Connection_ReNumber( __DDE_DDEConnection_)
   if (__DDE_Server = "") {
      Loop, Parse, __DDEM___DDE_Topic_List ,`n
      {
         if !A_loopfield
            continue
          if regexmatch(A_Loopfield, "(?<__DDE_Topic>[^|]++)"
                           . "\|"
                           . "(?<__DDE_Server>[^|]++)"
                           . "\|"
                           . "(?<DDEConnection>[^|]++)",Kill_) and (!__DDE_No_mass_Termination)
            DDE_KILL(Kill___DDE_Server,Kill___DDE_Topic, Kill_DDEConnection - 20)
      }
      
      __DDEM___DDE_Topic_List =
      __DDEConnection_Table =
      
      return
   }
   
   if (__DDE_Topic = "")
      __DDE_Topic := __DDE_Server   
   
   if InStr(__DDE_Topic . __DDE_Server, "\E" , 1) {
      s__DDE_Topic := regexreplace(__DDE_Topic,"\\E","\E\\E\Q")
      s__DDE_Server := regexreplace(__DDE_Server,"\\E","\E\\E\Q")
   } else {
      s__DDE_Topic = %__DDE_Topic%
      s__DDE_Server = %__DDE_Server%
   }

   if regexmatch(__DDEM___DDE_Topic_List
            , "\n\Q"
            . s__DDE_Topic
            . "|"
            . s__DDE_Server
            .  "|"
            . __DDE_DDEConnection_
            . "|\E"
            . "(?<__DDE_Server_ID>[^|]++)"
            . "\|"
            . "(?<Client_ID>[^`n]++)", __DDEM_) 

      __DDEM___DDE_Topic_List := regexreplace(__DDEM___DDE_Topic_List,"x)"
                              . "\n"
                              . "[^|]+"  ; __DDE_Topic
                              . "\|"
                              . "[^|]+"  ; __DDE_Server
                              .  "\|"
                              . "[^|]+"  ; any connection.. (is same id#s!!!)
                              . "\Q|"
                              . __DDEM___DDE_Server_ID
                              . "|"
                              . __DDEM_Client_ID
                              . "\E(?=\n|$)" , "") 

   if __DDE_Kill_window_on_Kill and  !regexmatch(__DDEM___DDE_Topic_List, "x)"
                              . "\n"
                              . "[^|]+"
                              . "\|"
                              . "[^|]+"
                              .  "\|\Q"
                              . __DDE_DDEConnection_
                              . "|\E" , __DDEM_)   
   {
      __DDEConnection_Table := regexreplace(__DDEConnection_Table,"\n\Q" . __DDE_DDEConnection_ . "|\E[^`n]++","")
      if ( __DDE_DDEConnection_ > 20) and ( __DDE_DDEConnection_ < 51)
         Gui, %__DDE_DDEConnection_%: destroy
   }
   
   if __DDEM___DDE_Server_ID {
      if !__DDE_UseAHKMessages
         DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_TERMINATE , "UInt", hAHK , "Int", 0)
      else {
         DetectHiddenWindows, On 
         PostMessage,   WM_DDE_TERMINATE,  hAHK  , 0,,ahk_id %__DDEM___DDE_Server_ID%
      }
   } else
   
      return  DDE__Tooltips( " ++DDE UNDEFINED TERMINATION ERROR ". __DDE_Topic . "|" . __DDE_Server . "  " . __DDE_DDEConnection_ " ** "  , 4000,3)

   if __DDEM___DDE_Topic_List
      regexmatch(__DDEM___DDE_Topic_List, "\n"
               . "[^|]++"               
               . "\|"
               . "[^|]++"
               . "\|"
               .  "(?<Connection_>[^|]++)"
               . "\|"
               .  "(?<M___DDE_Server_ID>[^|]++)"
                .  "\|"
               . "(?<M_Client_ID>[^|]++)"
               . "$", DDE)
   
   if __DDEM___DDE_Server_ID
      __Conversation_BASE := __DDE_DDEConnection_ 
                     . "|"
                     .  hex ( __DDEM___DDE_Server_ID )
                     . "|"
                     . hex ( __DDEM_Client_ID )

}


DDE_exitapp() {
   DDE_KILL()
   exitapp
}

DDE__Tooltips(text = "", delay = 3000, id = 1, x = "", y = "") {

   if (id < 1) or (id > 5)
      id := 1
   delay := (delay) ? delay : 3000
   ToolTip, %text%,%x%,%y%,%id%
   if text
      SetTimer, DDE__RemoveToolTip%id%, %delay%
   return 1  ; ie. is set
}
DDE__RemoveToolTips(id = 1) {
   if (id < 1) or (id > 5)
      id := 1
   SetTimer, DDE__RemoveToolTip%id%, Off
   ToolTip,,,,%id%
return


DDE__RemoveToolTip1:
   DDE__RemoveToolTips(2)
return


DDE__RemoveToolTip2:
   DDE__RemoveToolTips(2)
return

DDE__RemoveToolTip3:
   DDE__RemoveToolTips(3)
return

DDE__RemoveToolTip4:
   DDE__RemoveToolTips(4)
return

DDE__RemoveToolTip5:
   DDE__RemoveToolTips(5)
return

}


DDE_hex(HEXME_number) {

   if ((format := A_FormatInteger) != "H") {
      SetFormat Integer, Hex 
      HEXME_number := round(HEXME_number) + 0
      SetFormat Integer, %format% 
      return HEXME_number
   } else
      return HEXME_number + 0
}



;
; v1.07  [8/8/07]
;
; Copr. 2007.  See License.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; END OF DDE CLIENT WRAPPER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

_________________
Joyce Jamce


Last edited by Joy2DWorld on Mon Nov 19, 2007 12:37 am; edited 4 times in total
Back to top
View user's profile Send private message
engunneer



Joined: 30 Aug 2005
Posts: 6551
Location: Pacific Northwest, US

PostPosted: Thu Aug 09, 2007 4:37 pm    Post subject: Reply with quote

This looks like it could be quite useful. Can you give examples of simple things in text and then code? (I see the code, but no explanation of what it will do).

Any chance you can comment this with Natural Docs? This could then be wrapped into a standard library.

Thanks for this code. It shows much promise for answering common questions in the help forum, like Sean did with CoHelper
_________________
Unless otherwise noted, all code is untested.
Common Answers: 1.(Loops, Viruses, etc.) 2. Search 3.RTFM
Back to top
View user's profile Send private message Visit poster's website
Washboard
Guest





PostPosted: Thu Aug 09, 2007 5:23 pm    Post subject: Re: AHK DDE FUNCTIONS. (Including a/sync,batch,multi-channe Reply with quote

Joy2DWorld wrote:
note it uses windows (above gui 20), for the connections.
DDE needs this.
Knowing that :
Help File wrote:
Each script may have up to 99 GUI windows simultaneously
is DDE really needs 79 windows (99 - 20) to work ? Isn't it too much ?
Back to top
Sean



Joined: 12 Feb 2007
Posts: 1281

PostPosted: Fri Aug 10, 2007 2:41 am    Post subject: Re: AHK DDE FUNCTIONS. (Including a/sync,batch,multi-channe Reply with quote

Joy2DWorld wrote:
AHK should (with this) now have full DDE CLIENT FUNCTIONALITY. In fact, MORE FUNCTIONALITY than the windows DDE DLL (you are not limited to one conversation per topic per server.., full async and batch support).

Although I haven't tried it yet, lacking time, looks like you've done a great job!
Thanks for sharing it.
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 417
Location: Galil, Israel

PostPosted: Fri Aug 10, 2007 6:32 am    Post subject: Re: AHK DDE FUNCTIONS. (Including a/sync,batch,multi-channe Reply with quote

Washboard wrote:
is DDE really needs 79 windows (99 - 20) to work ? Isn't it too much ?



like this:


DDE needs NO windows to work if you use the MS DLL gateway.

but DDE was designed based on windows messaging...

the power of the protocal stems from this.....



expect for majority of ahk users.. will need only single gui window (gui #20),


but.. if you have need to multi-thread conversation with server:

ie. same server, same topic,

the *only* way you can do that (and you CANNOT DO THAT IN THE SAME PROGRAM THREAD USING MS DLL GATEWAY), is to open additional windows --> the window's handle is your conversation 'address'...

to have more than one conversation with the same server&topic, need more than one address...


if you want 30 connections to same server.. (for batch async interaction, for example) the cost of 30 gui's is cheap!



hope this helps clarify!
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 417
Location: Galil, Israel

PostPosted: Fri Aug 10, 2007 6:47 am    Post subject: Reply with quote

@Sean,

(it's working 100% due to your help in pointing out the way... my mind and MS just not in same sync universe...)


and,

is (as promised), PRO-ACTIVE.... DDE...


can work same as calling the DDEML, ie.. in-line, etc...

(only better as

allows in same AHK thread MULTIPLE connections,

and..

async or sync data transfer....
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Washboard
Guest





PostPosted: Fri Aug 10, 2007 5:08 pm    Post subject: Re: AHK DDE FUNCTIONS. (Including a/sync,batch,multi-channe Reply with quote

Joy2DWorld wrote:
if you want 30 connections to same server.. (for batch async interaction, for example) the cost of 30 gui's is cheap!



hope this helps clarify!
Yes I understand the reason of the use of trhe gui windows. May be would it be sufficent in the majority of cases to use the 50 last ones ? Thanks for your answer.
Back to top
Joy2DWorld



Joined: 04 Dec 2006
Posts: 417
Location: Galil, Israel

PostPosted: Fri Aug 10, 2007 5:20 pm    Post subject: Re: AHK DDE FUNCTIONS. (Including a/sync,batch,multi-channe Reply with quote

Washboard wrote:
Yes I understand the reason of the use of trhe gui windows. May be would it be sufficent in the majority of cases to use the 50 last ones ? Thanks for your answer.



most will need only 1...


you can have thousands of conversations,

millions of dde transactions...

on just 1 window.



only when you want expanded path to,
say,

same excel spreadsheet,


instead of 1 thing at a time,

can do multi....
_________________
Joyce Jamce
Back to top
View user's profile Send private message
qtmspin



Joined: 17 Oct 2006
Posts: 22

PostPosted: Wed Feb 06, 2008 2:35 am    Post subject: Not sure how to do this... Reply with quote

I have done a few different 'permutations' of the code to get the prices, however I am not getting past the connection.

msgbox % DDE_Connect("MT4","QUOTE") --> this works

None of these are sucessful... does anyone have a suggestion?
msgbox % DDE_Request("USDCHF") "`n" DDE_Request("[USDCHF]")
msgbox % DDE_Poke("[EURUSD]", "[QUOTE]")
msgbox % DDE_Execute("[""USDCHF""]")

Thanks,
Matt

other example of another language using the Metatrader DDE
http://www.4xlab.net/cs/forums/136/ShowPost.aspx
----
DdeClient client = new DdeClient("MT4", "QUOTE");
client.Disconnected += OnDisconnected;
client.Connect();
client.StartAdvise("USDCHF", 1, true, 60000);
client.StartAdvise("EURUSD", 1, true, 60000);
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 417
Location: Galil, Israel

PostPosted: Fri Feb 08, 2008 12:43 am    Post subject: Reply with quote

you're looking for a stream of info,

auto updates sent as data changes (dde_ADVISE)


it is indeed cool stuff, but for MS Word, MS Excel, etc., not much use for it.


It is not included now in the DDE_ functions (Advise/Unadvise).



Sean has a DDEML version of DDE (search forum you will find) that you can also set up a ADVISE transaction [although likely might be complex for you, but really is not so much so].


Personally find the DDE control of word and excell to be very easy, more so than using COM controls for many tasks. but, seems I am in minority and not very many seem interested in DDE functions.


advise is--> check to see if you can just request info instead of getting update stream.

if so, you can use the dde_request

otherwise, you need to build your own advise based code, which , basically, event driven from the data sends you'll get back.



hope this helps.
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 417
Location: Galil, Israel

PostPosted: Fri Feb 08, 2008 12:54 am    Post subject: Reply with quote

ps: if you want to code an dde_advise funtion,

I will walk you thru/guide you if you desire.


ps2: Sean's dde has a ADVISE (using the ddeml), and you can see how he uses callback to catch updates. [if this makes sense to you].
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Swan
Guest





PostPosted: Fri Mar 07, 2008 8:20 am    Post subject: DDE Batch Mode Reply with quote

I'm trying to use the batch mode.
I'm not getting an answer directly from comands ( := Request('[xxxx' ) but i'm not able to get anything from __DDE_Answer_Table (it contains always balnks).
How can I access the dderequest results?
Can you explain how to build an dde_advise function to be placed in your module?

Thanx
Back to top
Joy2DWorld



Joined: 04 Dec 2006
Posts: 417
Location: Galil, Israel

PostPosted: Fri Mar 07, 2008 3:06 pm    Post subject: Reply with quote

1. Batch Mode:


if in function make sure all these are Global


__DDE_BatchMode or __DDE_Use_Answer_Table set true

__DDE__myAnswer_Table as global var returns table

for batch, recommend to use safe mode as well.


2. for advise:


i gave qtmspin some direction... maybe he will post his final code ??


if this helps:

Code:
if get Error: Caller must pass a variable to this ByRef parameter.

Line #
003: idInst := DDEML_Initialize(xxx, RegisterCallback("DDE_Callback"))


--->

xxx = 0

xx := RegisterCallback("DDE_Callback")

idInst := DDEML_Initialize(xxx,xx)



something like this should work: [am just using sean's DDE.ahk]

#Persistant

idInst := DDEML_Initialize(0, RegisterCallback("DDE_Callback"))
hConv := DDE_Connect(idInst, "SERVER", "TOPIC")
DDEML_PostAdvise(idInst, "TOPIC","ITEM")


; or maybe
; DDEML_ClientTransaction(idInst, hConv, "ADVSTART", "SUBJECT")


DDE_Callback(nType, nFormat, hConv, hString1, hString2, hData, nData1, nData2)
{
msgbox % DDE_GetData(hData)

}


DDEML_Initialize(ByRef idInst = 0, pCallback = 0, nFlags = 0)
{
If DllCall("DdeInitialize", "UintP", idInst, "Uint", pCallback, "Uint", nFlags, "Uint", 0)=0
Return idInst
}

DDEML_Uninitialize(idInst)
{
Return DllCall("DdeUninitialize", "Uint", idInst)
}

DDEML_GetLastError(idInst)
{
Return DllCall("DdeGetLastError", "Uint", idInst)
}

DDEML_NameService(idInst, sServ = "", nCmd = 1)
{
Return DllCall("DdeNameService", "Uint", idInst, "Uint", hServ:=sServ="" ? 0 : DDEML_CreateStringHandle(idInst,sServ), "Uint", 0, "Uint", nCmd), hServ ? DDEML_FreeStringHandle(idInst,hServ) : ""
}

DDEML_EnableCallback(idInst, hConv = 0, nCmd = 0)
{
Return DllCall("DdeEnableCallback", "Uint", idInst, "Uint", hConv, "Uint", nCmd)
}

DDEML_PostAdvise(idInst, sTopic = "", sItem = "")
{
Return DllCall("DdePostAdvise", "Uint", idInst, "Uint", hTopic:=sTopic="" ? 0 : DDEML_CreateStringHandle(idInst,sTopic), "Uint", hItem:=sItem="" ? 0 : DDEML_CreateStringHandle(idInst,sItem))
, hTopic ? DDEML_FreeStringHandle(idInst,hTopic) : ""
, hItem ? DDEML_FreeStringHandle(idInst,hItem) : ""
}

DDEML_ClientTransaction(idInst, hConv, sType = "EXECUTE", sItem = "", pData = 0, cbData = 0, CF_Format = 1, bAsync = False)
{
; XTYPF_NODATA := 0x0004
; XTYPF_ACKREQ := 0x0008
XTYP_ADVSTART := 0x1030
XTYP_ADVSTOP := 0x8040
XTYP_EXECUTE := 0x4050
XTYP_POKE := 0x4090
XTYP_REQUEST := 0x20B0
Return DllCall("DdeClientTransaction", "Uint", pData, "Uint", cbData, "Uint", hConv, "Uint", hItem:=sItem="" ? 0 : DDEML_CreateStringHandle(idInst,sItem), "Uint", CF_Format, "Uint", XTYP_%sType%, "Uint", bAsync ? TIMEOUT_ASYNC:=-1 : 10000, "UintP", nResult), hItem ? DDEML_FreeStringHandle(idInst,hItem) : ""
}

DDEML_AbandonTransaction(idInst, hConv = 0, idTransaction = 0)
{
Return DllCall("DdeAbandonTransaction", "Uint", idInst, "Uint", hConv, "Uint", idTransaction)
}

DDEML_Connect(idInst, sServ = "", sTopic = "", pCC = 0)
{
Return DllCall("DdeConnect", "Uint", idInst, "Uint", hServ:=sServ="" ? 0 : DDEML_CreateStringHandle(idInst,sServ), "Uint", hTopic:=sTopic="" ? 0 : DDEML_CreateStringHandle(idInst,sTopic), "Uint", pCC)
, hTopic ? DDEML_FreeStringHandle(idInst,hTopic) : ""
, hServ ? DDEML_FreeStringHandle(idInst,hServ) : ""
}

DDEML_Reconnect(hConv)
{
Return DllCall("DdeReconnect", "Uint", hConv)
}

DDEML_Disconnect(hConv)
{
Return DllCall("DdeDisconnect", "Uint", hConv)
}

DDEML_ConnectList(idInst, sServ = "", sTopic = "", hConvList = 0, pCC = 0)
{
Return DllCall("DdeConnectList", "Uint", idInst, "Uint", hServ:=sServ="" ? 0 : DDEML_CreateStringHandle(idInst,sServ), "Uint", hTopic:=sTopic="" ? 0 : DDEML_CreateStringHandle(idInst,sTopic), "Uint", hConvList, "Uint", pCC)
, hTopic ? DDEML_FreeStringHandle(idInst,hTopic) : ""
, hServ ? DDEML_FreeStringHandle(idInst,hServ) : ""
}

DDEML_DisconnectList(hConvList)
{
Return DllCall("DdeDisconnectList", "Uint", hConvList)
}

DDEML_QueryNextServer(hConvList, hConvPrev = 0)
{
Return DllCall("DdeQueryNextServer", "Uint", hConvList, "Uint", hConvPrev)
}

DDEML_QueryConvInfo(hConv, idTransaction = -1, ByRef ci = "") ; QID_SYNC=-1
{
Return DllCall("DdeQueryConvInfo", "Uint", hConv, "Uint", idTransaction, "Uint", NumPut(VarSetCapacity(ci,64,0),ci)-4)
}

DDEML_AccessData(hData, ByRef cbData = "")
{
Return DllCall("DdeAccessData", "Uint", hData, "UintP", cbData)
}

DDEML_UnaccessData(hData)
{
Return DllCall("DdeUnaccessData", "Uint", hData)
}

DDEML_AddData(hData, pData, cbData, cbOff = 0)
{
Return DllCall("DdeAddData", "Uint", hData, "Uint", pData, "Uint", cbData, "Uint", cbOff)
}

DDEML_GetData(hData, ByRef sData = "", cbOff = 0)
{
cb := DllCall("DdeGetData", "Uint", hData, "Uint", 0, "Uint", 0, "Uint", cbOff)
VarSetCapacity(sData, cb)
If DllCall("DdeGetData", "Uint", hData, "str", sData, "Uint", cb, "Uint", cbOff)
Return sData
}

DDEML_QueryString(idInst, hString, nCodePage = 1004) ; CP_WINANSI = 1004, CP_WINUNICODE = 1200
{
cch := DllCall("DdeQueryString", "Uint", idInst, "Uint", hString, "Uint", 0, "Uint", 0, "int", nCodePage)
VarSetCapacity(sString, cch)
If DllCall("DdeQueryString", "Uint", idInst, "Uint", hString, "str", sString, "Uint", cch+1, "int", nCodePage)
Return sString
}

DDEML_CreateDataHandle(idInst, sItem = "", pData = 0, cbData = 0, cbOff = 0, CF_Format = 1, bOwned = True)
{
Return DllCall("DdeCreateDataHandle", "Uint", idInst, "Uint", pData, "Uint", cbData, "Uint", cbOff, "Uint", hItem:=sItem="" ? 0 : DDEML_CreateStringHandle(idInst,sItem), "Uint", CF_Format, "Uint", bOwned ? HDATA_APPOWNED:=1 : 0), hItem ? DDEML_FreeStringHandle(idInst,hItem) : ""
}

DDEML_FreeDataHandle(hData)
{
Return DllCall("DdeFreeDataHandle", "Uint", hData)
}

DDEML_CreateStringHandle(idInst, sString, nCodePage = 1004)
{
Return DllCall("DdeCreateStringHandle", "Uint", idInst, "Uint", &sString, "int", nCodePage)
}

DDEML_KeepStringHandle(idInst, hString)
{
Return DllCall("DdeKeepStringHandle", "Uint", idInst, "Uint", hString)
}

DDEML_FreeStringHandle(idInst, hString)
{
Return DllCall("DdeFreeStringHandle", "Uint", idInst, "Uint", hString)
}

DDEML_CmpStringHandles(hString1, hString2)
{
Return DllCall("DdeCmpStringHandles", "Uint", hString1, "Uint", hString2)
}

DDEML_SetUserHandle(hConv, hUser)
{
Return DllCall("DdeSetUserHandle", "Uint", hConv, "Uint", -1, "Uint", hUser)
}


note: this is just Sean's code using DDEML (not message system.) Not allow for multi-connection to same server, etc. *BUT* with advise am not seeing how that would be relevant... ie. the DDEML should work fine for advise...

remember... unlike the DDE_Request() functrion structure, ADVISE is interrupt driven...
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group