         title   Utility

         .586
         .model flat, stdcall
         option casemap:none   ; Case sensitive

            include  L:\AsmEdit\include\windows.inc
            include  user32.inc
            include  kernel32.inc
            include  advapi32.inc

            include  L:\AsmEdit\include\DSPMACRO.asm

         includelib  user32.lib
         includelib  kernel32.lib
         includelib  advapi32.lib

;===================================================
; PROTO, MACRO, and Data section
;===================================================
Initilize    PROTO   :DWORD
NewProject   PROTO   :DWORD
GetRegPaths  PROTO   :DWORD

.const
; Messages sent to AsmEdit
IDM_GETDWORDS           equ WM_USER+50
IDM_GETDATA             equ WM_USER+52

IDM_NEW                 equ 6001
IDM_OPEN                equ 6002
IDM_SAVEAS              equ 6004
IDM_CLOSETAB            equ 6007
IDM_PRINT               equ 6010

; IDM_GETDWORDS returns a poiner to the following handle & dword structure.
ASMDWORDS struct
      hWnd              dd 0   ; Handle of AsmEdit.
      hMenu             dd 0   ; Handle of AsmEdit Menu.
      hToolBar          dd 0   ; Handle of AsmEdit ToolBar.
      hStatus           dd 0   ; Handle of AsmEdit StatusBar.
      hREdit            dd 0   ; Handle of RichEdit window.
      MenuID            dd 0   ; Addin/Project menu item ID.
      ProjType          dd 0   ; Project type from create (.dll or .exe)
ASMDWORDS ends

; IDM_GETDATA returns a poiner to the following data structure.
ASMEDATA struct
      lpRootDir         dd 0   ; Pointer to root dir, ends with \.
      lpPathName        dd 0   ; Pointer to current full path.
      lpFileName        dd 0   ; Pointer to FileName.
      lpProjectName     dd 0   ; Pointer to Project Name.
      lpProjectPath     dd 0   ; Pointer to Project Path.
      lpRegKey          dd 0   ; AsmEdit registry key.
ASMEDATA ends

.data
AppName                 db 'Project',0
szREGSZ                 db 'REG_SZ',0
szAddinKey              db '\User Addin Items',0
szMenuCnt               db 'Menu Cnt',0
rDEFr                   db '[Default Root Dir]',0
rDEFb                   db '[Default Bin Dir.]',0
rDEFi                   db '[Default Inc Dir.]',0
rDEFp                   db '[Default Proj Dir]',0

szDllProc               db 'DllProc',0
szDLL                   db '.dll',0
szEXE                   db '.exe',0
szCol                   db ':',0
szCom                   db ',',0
szCar                   db '^',0

szName                  db 'Menu '
szIndex                 db 10 dup(?),0
fmat4                   db '%04u',0

szBuffS                 db 64  dup(?)
szBuffN                 db 64  dup(?)
szBuffF                 db 64  dup(?)
szBuffW                 db 64  dup(?)
szBuffP                 db 256 dup(?)
szPath                  db 256 dup(?)

szTest                  db 'This is a test',0
szERR01                 db 'The menu item already exist',0dh,0ah
                        db 'and will not be added.',0
.data?
hInst                   dd ?   ; AsmEdit's instance
hREdit                  dd ?   ; Handle of RichEdit
dwHStruct               dd ?   ; Pointer to handles struct
dwPStruct               dd ?   ; Pointer to proc struct
dwDStruct               dd ?   ; Pointer to data struct

lpType                  dd ?
lpcbData                dd ?
lpdwDisp                dd ?

szBuff                  db 256 dup(?)

.code

;===============================================================
; DLL Entry
;===============================================================
DllEntry PROC hInstance:DWORD, reason, reserved1

      .if reason == DLL_PROCESS_ATTACH  ; When the dll is loaded
           push     hInstance
            pop     hInst
            mov     eax, TRUE
      .endif
         ret

DllEntry ENDP

;===============================================================
; Initilize Proc loads pointers for various handles, and
; data areas. Is called from the DllProc
;===============================================================
Initilize PROC  hWnd:DWORD

      INVOKE     SendMessage, hWnd, IDM_GETDWORDS, 0, 0
         mov     dwHStruct, eax
      INVOKE     SendMessage, hWnd, IDM_GETDATA, 0, 0
         mov     dwDStruct, eax
         ret

Initilize ENDP

;===============================================================
; DllProc Receives WM_COMMAND messages from AsmEdit
;===============================================================
DllProc PROC  uses edi hWnd:DWORD, wMsg, wParam, lParam
LOCAL    cr:CHARRANGE
LOCAL    hMem:DWORD, Chg ;, hMenu, hSMenu, hKey, mItem, PType
;LOCAL    szMenuItems[256]:BYTE

      .if wMsg == WM_COMMAND

         .if !dwHStruct                  ; Check if Initilize has been run
            INVOKE     Initilize, hWnd
         .endif
            mov     eax, wParam
           cwde
            mov     wParam, eax

;---------- [New Tab] ----------
         .if eax == 1
            INVOKE     SendMessage, hWnd, WM_COMMAND, IDM_NEW, 0
               jmp     Ret1

;---------- [Open File] ----------
         .elseif eax == 2 && !lParam
            INVOKE     SendMessage, hWnd, WM_COMMAND, IDM_OPEN, 0
               jmp     Ret1

;---------- [Close Tab] ----------
         .elseif eax == 2 && lParam == 1
            INVOKE     SendMessage, hWnd, WM_COMMAND, IDM_CLOSETAB, 0
               jmp     Ret1

;---------- [Save As] ----------
         .elseif eax == 3
            INVOKE     SendMessage, hWnd, WM_COMMAND, IDM_SAVEAS, 0
               jmp     Ret1

;---------- [Print] ----------
         .elseif eax == 4
            INVOKE     SendMessage, hWnd, WM_COMMAND, IDM_PRINT, 0
               jmp     Ret1

;---------- [Get a Path from the registry] ----------
         .elseif eax == 5
            INVOKE     GetRegPaths, lParam  ; 0 = Root, 1 = Hutch's, 2 = Inc, 3 = Project
INVOKE     MessageBox, NULL, addr szBuff, addr szTest, MB_OK
               jmp     Ret1

;---------- Display the PathName from AsmEdit] ----------
         .elseif eax == 6
               mov     eax, dwDStruct
               mov     ecx, (ASMEDATA ptr [eax]).lpPathName
INVOKE     MessageBox, NULL, ecx, addr szTest, MB_OK
               jmp     Ret1

;---------- [New Project] ----------
         .elseif eax == 22
            INVOKE     NewProject, hWnd
               jmp     Ret1

;---------- [Change Case] ----------
         .elseif eax >= 41 && eax <= 43
;---------- [Get current handle of hREdit] ----------
               mov     eax, dwHStruct
               mov     eax, (ASMDWORDS ptr [eax]).hREdit
               mov     hREdit, eax
            INVOKE     SendMessage, hREdit, EM_GETMODIFY, 0, 0
               mov     Chg, eax
            INVOKE     SendMessage, hREdit, EM_EXGETSEL, 0, addr cr
               mov     eax, cr.cpMax
               sub     eax, cr.cpMin
               inc     eax
            INVOKE     GlobalAlloc, GMEM_FIXED or GMEM_ZEROINIT, eax
               mov     hMem, eax
            INVOKE     GlobalLock, hMem
            INVOKE     SendMessage, hREdit, EM_GETSELTEXT, 0, hMem
               mov     edi, hMem

LoopCase:
               mov     al, [edi]
            .if wParam == 41
               .if al >= 'A' && al <= 'Z'
                      or     byte ptr [edi], 20h
               .elseif al >= 'a' && al <= 'z'
                     and     byte ptr [edi], 0dfh
               .endif
            .elseif wParam == 42
               .if al >= 'A' && al <= 'Z'
                      or     byte ptr [edi], 20h
               .endif
            .elseif wParam == 43
               .if al >= 'a' && al <= 'z'
                     and     byte ptr [edi], 0dfh
               .endif
            .endif
               inc     edi
                or     al, al
               jne     LoopCase
            INVOKE     SendMessage, hREdit, EM_REPLACESEL, TRUE, hMem
            INVOKE     SendMessage, hREdit, EM_EXSETSEL, 0, addr cr
            INVOKE     GlobalUnlock, hMem
            INVOKE     GlobalFree, hMem
            INVOKE     SendMessage, hREdit, EM_SETMODIFY, Chg, 0
               jmp     Ret1

         .endif

;---------- [AsmEdit closing down] ----------
      .elseif wMsg == WM_CLOSE
; Do cleanup work
            jmp     Ret0
      .endif

Ret0:
         xor     eax, eax
         ret

Ret1:
         mov     eax, TRUE
         ret

DllProc ENDP

;===============================================================
; Create a new project and menu item
;===============================================================
NewProject PROC uses ebx hWnd:DWORD
LOCAL    hMenu:DWORD, hSMenu, Cnt, mItem, MenuID, hKey, PType, Incr, PopupM, hSubMenu
LOCAL    szMenuItems[256]:BYTE

         mov     eax, dwDStruct
         mov     ecx, (ASMEDATA ptr [eax]).lpProjectName
      INVOKE     lstrcpy, addr szBuffS, ecx    ; Save project name
      INVOKE     SendMessage, hWnd, WM_COMMAND, 902, 21
         mov     eax, dwDStruct
         mov     ecx, (ASMEDATA ptr [eax]).lpProjectName
      INVOKE     lstrlen, ecx
      .if !eax
            mov     eax, dwDStruct
            mov     ecx, (ASMEDATA ptr [eax]).lpProjectName
         INVOKE     lstrcpy, ecx, addr szBuffS ; Restore project name
            jmp     Ret1
      .endif
;---------- [See if create project was canceled] ----------
         mov     eax, dwDStruct
         mov     ecx, (ASMEDATA ptr [eax]).lpProjectPath
      INVOKE     lstrcpy, addr szBuffP, ecx   ; New path string
      INVOKE     lstrlen, addr szBuffP
      .if !eax
            jmp     Ret1
      .endif

;---------- [Get the new Project name & menu handles] ----------
         mov     eax, dwDStruct
         mov     ecx, (ASMEDATA ptr [eax]).lpProjectName
      INVOKE     lstrcpy, addr szBuffN, ecx   ; New project name
         mov     eax, dwHStruct
         mov     ecx, (ASMDWORDS ptr [eax]).hMenu
         mov     hMenu, ecx                   ; Main menu handle
      INVOKE     GetMenuItemCount, hMenu      ; Get count of items in the menu bar
         sub     eax, 3
      INVOKE     GetSubMenu, hMenu, eax       ; Get handle to last-3 submenu
         mov     hSMenu, eax

;---------- [Check for duplicate menu item] ----------
      INVOKE     GetMenuItemCount, hSMenu     ; Get count of items in the submenu
         mov     Cnt, eax
         and     mItem, 0
      .while (Cnt)
         INVOKE     GetMenuString, hSMenu, mItem, addr szBuff, lengthof szBuff, MF_BYPOSITION
         INVOKE     lstrcmp, addr szBuffN, addr szBuff
         .if eax == 0
            INVOKE     MessageBox, NULL, addr szERR01, addr AppName, MB_OK
               jmp     Ret1
         .endif
            inc     mItem
            dec     Cnt
      .endw

;---------- [Get the menu item insertion ID] ----------
         mov     eax, dwHStruct
         mov     ecx, (ASMDWORDS ptr [eax]).MenuID
         inc     ecx
         mov     MenuID, ecx

;---------- [Build the dll or exe path] ----------
      INVOKE     lstrcat, addr szBuffP, addr szBuffN
         mov     eax, dwHStruct
         mov     ecx, (ASMDWORDS ptr [eax]).ProjType
         mov     PType, ecx                   ; New project type (.dll or .exe)
      .if ecx
         INVOKE     lstrcat, addr szBuffP, addr szDLL
      .else
         INVOKE     lstrcat, addr szBuffP, addr szEXE
      .endif
   
;---------- [Open or create the menu registry] ----------
         mov     eax, dwDStruct
         mov     ecx, (ASMEDATA ptr [eax]).lpRegKey
      INVOKE     lstrcpy, addr szMenuItems, ecx
      INVOKE     lstrcat, addr szMenuItems, addr szAddinKey
   
      INVOKE     RegCreateKeyEx, HKEY_CURRENT_USER, addr szMenuItems, 0, offset szREGSZ, 0,\
                 KEY_WRITE or KEY_READ, 0, addr hKey, addr lpdwDisp
   
         mov     lpcbData, 4
      INVOKE     RegQueryValueEx, hKey, addr szMenuCnt, 0, addr lpType, addr mItem, addr lpcbData
      .if eax != 0 || mItem == 3100
;Do error msg
            jmp     Ret1
      .endif
         inc     mItem
      INVOKE     RegSetValueEx, hKey, addr szMenuCnt, 0, REG_DWORD, addr mItem, 4

;---------- [Resequence the registry] ----------
         mov     ecx, mItem
         mov     Incr, ecx
         dec     Incr
      .while (ecx > MenuID)
         INVOKE     wsprintf, addr szIndex, addr fmat4, Incr
            mov     lpcbData, 250
         INVOKE     RegQueryValueEx, hKey, addr szName, 0, addr lpType, addr szBuff, addr lpcbData
            inc     Incr
         INVOKE     wsprintf, addr szIndex, addr fmat4, Incr
            mov     lpcbData, 250
         INVOKE     RegSetValueEx, hKey, addr szName, 0, REG_SZ, addr szBuff, lpcbData
            dec     Incr
            mov     ecx, Incr
            dec     Incr
      .endw
         inc     Incr

;---------- [Build the Registry entry for the new menu item] ----------
      INVOKE     wsprintf, addr szIndex, addr fmat4, Incr ;mItem
      INVOKE     lstrcat, addr szPath, addr szBuffN     ; New project name
      INVOKE     lstrcat, addr szPath, addr szCol
      INVOKE     lstrcat, addr szPath, addr szBuffP     ; New project path
      .if PType                                         ; DLL
         INVOKE     lstrcat, addr szPath, addr szCol
         INVOKE     lstrcat, addr szBuffF, addr szDllProc
         INVOKE     lstrcat, addr szPath, addr szBuffF  ; Function name
         INVOKE     lstrcat, addr szPath, addr szCol
            mov     dword ptr szBuffW, 00392c39h        ; = 9,9
         INVOKE     lstrcat, addr szPath, addr szBuffW  ; wParam & lParm values
      .endif
      INVOKE     lstrcat, addr szPath, addr szCar
      INVOKE     lstrlen, addr szPath
         mov     lpcbData, eax
      INVOKE     RegSetValueEx, hKey, addr szName, 0, REG_SZ, addr szPath, lpcbData

;---------- [Delete Addin menu items] ----------
      INVOKE     GetMenuItemCount, hSMenu
         mov     Cnt, eax
      .while (Cnt)
         INVOKE     DeleteMenu, hSMenu, 1, MF_BYPOSITION
            dec     Cnt
      .endw

;---------- [Rebuild the Addin menu from the registry] ----------
         and     PopupM, 0
        push     mItem
         pop     Cnt
         sub     Cnt, 3100
         mov     mItem, 3101
      .while (Cnt)
         INVOKE     wsprintf, addr szIndex, addr fmat4, mItem
            mov     lpcbData, 250
         INVOKE     RegQueryValueEx, hKey, addr szName, 0, addr lpType, addr szPath, addr lpcbData
         .if eax == 0
               xor     eax, eax
               mov     ecx, 150
            .while (ecx)
               .if byte ptr szPath[eax] == ':' || byte ptr szPath[eax] == '*' &&\
                   byte ptr szPath[eax+5] == 'S' || byte ptr szPath[eax] == 'S' &&\
                   byte ptr szPath[eax+2] == 'B' && byte ptr szPath[eax+7] == ':'
                  .if byte ptr szPath[eax] == ':'
                       push     eax
                        inc     eax
                     INVOKE     lstrcpyn, addr szBuffN, addr szPath, eax
                        pop     eax
                        inc     eax
                     INVOKE     lstrcpy, addr szBuffP, addr szPath[eax]
                  .elseif byte ptr szPath[eax+2] == 'B' && byte ptr szPath[eax+7] == ':'
                     INVOKE     lstrcpy, addr szBuffN, addr szPath+9
                     INVOKE     CreatePopupMenu
                        mov     hSubMenu, eax
                        mov     PopupM, 1
                  .else
                     INVOKE     lstrcpy, addr szBuffN, addr szPath
                  .endif
                   push     hSMenu
                  INVOKE     lstrlen, addr szPath
                  .if byte ptr szPath[eax-1] == '^'
                       push     hSubMenu
                        pop     hSMenu
                  .endif
                  INVOKE     GetMenuItemCount, hSMenu  ; Get count of items in the sub menu
                  .if eax == -1
                        mov     eax, 0
                  .endif
                     mov     ecx, eax
;                  .if byte ptr szPath == '*'
                  .if byte ptr szPath == '-' && byte ptr szPath+2 == '-'
                     INVOKE     InsertMenu, hSMenu, ecx, MF_BYPOSITION or MF_SEPARATOR, mItem, addr szBuffN
                  .elseif PopupM
                     INVOKE     InsertMenu, hSMenu, ecx, MF_BYPOSITION or\
                                MF_STRING or MF_POPUP, hSubMenu, addr szBuffN
                  .else
                     INVOKE     InsertMenu, hSMenu, ecx, MF_BYPOSITION or MF_STRING, mItem, addr szBuffN
                  .endif
                     and     PopupM, 0
                     pop     hSMenu
                  .break
               .endif
                  dec     ecx
                  inc     eax
            .endw
         .endif
            inc     mItem
            dec     Cnt
      .endw

Ret1:
         mov     eax, TRUE
         ret

NewProject ENDP

;===============================================================
; GetRegPaths Retrieves paths from the registry
;===============================================================
GetRegPaths PROC PathT:DWORD
LOCAL    hReg:DWORD, w1

         mov     eax, dwDStruct
         mov     ecx, (ASMEDATA ptr [eax]).lpRegKey
      INVOKE     RegCreateKeyEx, HKEY_CURRENT_USER, ecx, 0, addr szREGSZ, 0,\
                 KEY_WRITE or KEY_READ, 0, addr hReg, addr lpdwDisp
      .if eax
; Do error msg
            ret
      .endif

         mov     eax, PathT
        imul     eax, 19
         lea     ecx, rDEFr
         add     ecx, eax
         mov     lpcbData, 250
      INVOKE     RegQueryValueEx, hReg, ecx, 0, addr lpType, addr szBuff, addr lpcbData
      .if eax
; Do error msg
            ret
      .endif
      INVOKE     RegCloseKey, hReg

         ret

GetRegPaths ENDP

End DllEntry
