diff options
Diffstat (limited to 'plugins/ShlExt/shlcom.pas')
-rw-r--r-- | plugins/ShlExt/shlcom.pas | 4879 |
1 files changed, 2504 insertions, 2375 deletions
diff --git a/plugins/ShlExt/shlcom.pas b/plugins/ShlExt/shlcom.pas index 79a9134012..d693dcb300 100644 --- a/plugins/ShlExt/shlcom.pas +++ b/plugins/ShlExt/shlcom.pas @@ -1,2383 +1,2512 @@ unit shlcom; - -{$IFDEF FPC} - {$PACKRECORDS 4} - {$MODE Delphi} -{$ENDIF} - -interface - -uses - - Windows, m_globaldefs, shlipc, shlicons; - - {$define COM_STRUCTS} - {$define SHLCOM} - {$include shlc.inc} - {$undef SHLCOM} - {$undef COM_STRUCTS} - - function DllGetClassObject(const CLSID: TCLSID; const IID: TIID; - var Obj): HResult; stdcall; - function DllCanUnloadNow: HResult; stdcall; - - procedure InvokeThreadServer; - - procedure CheckRegisterServer; - - procedure CheckUnregisterServer; - - function RemoveCOMRegistryEntries: HResult; - - function ExtractIcon(hInst: THandle; pszExe: PChar; nIndex: Integer): HICON; stdcall; external 'shell32.dll' name 'ExtractIconA'; - -implementation - -var - - dllpublic: record - FactoryCount: Integer; - ObjectCount: Integer; - end; - - VistaOrLater: Boolean; - - {$include m_database.inc} - {$include m_clist.inc} - {$include m_protocols.inc} - {$include m_protosvc.inc} - {$include m_ignore.inc} - {$include m_skin.inc} - {$include m_file.inc} - {$include m_system.inc} - {$include m_langpack.inc} - {$include m_skin.inc} - {$include statusmodes.inc} - - {$define COMAPI} - {$include shlc.inc} - {$undef COMAPI} - - {$include m_helpers.inc} - -const - - IPC_PACKET_SIZE = $1000 * 32; - //IPC_PACKET_NAME = 'm.mi.miranda.ipc'; // prior to 1.0.6.6 - //IPC_PACKET_NAME = 'mi.miranda.IPCServer'; // prior to 2.0.0.9
- IPC_PACKET_NAME = 'm.mi.miranda.ipc.server'; - -const - -{ Flags returned by IContextMenu*:QueryContextMenu() } - - CMF_NORMAL = $00000000; - CMF_DEFAULTONLY = $00000001; - CMF_VERBSONLY = $00000002; - CMF_EXPLORE = $00000004; - CMF_NOVERBS = $00000008; - CMF_CANRENAME = $00000010; - CMF_NODEFAULT = $00000020; - CMF_INCLUDESTATIC = $00000040; - CMF_RESERVED = $FFFF0000; { view specific } - -{ IContextMenu*:GetCommandString() uType flags } - - GCS_VERBA = $00000000; // canonical verb - GCS_HELPTEXTA = $00000001; // help text (for status bar) - GCS_VALIDATEA = $00000002; // validate command exists - GCS_VERBW = $00000004; // canonical verb (unicode) - GC_HELPTEXTW = $00000005; // help text (unicode version) - GCS_VALIDATEW = $00000006; // validate command exists (unicode) - GCS_UNICODE = $00000004; // for bit testing - Unicode string - GCS_VERB = GCS_VERBA; // - GCS_HELPTEXT = GCS_HELPTEXTA; - GCS_VALIDATE = GCS_VALIDATEA; - -type - - { this structure is returned by InvokeCommand() } - - PCMInvokeCommandInfo = ^TCMInvokeCommandInfo; - TCMInvokeCommandInfo = packed record - cbSize: DWORD; - fMask: DWORD; - hwnd: HWND; - lpVerb: PChar; { maybe index, type cast as Integer } - lpParams: PChar; - lpDir: PChar; - nShow: Integer; - dwHotkey: DWORD; - hIcon: THandle; - end; - -{ completely stolen from modules.c: 'NameHashFunction' modified slightly } - - function StrHash(const szStr: PChar): DWORD; cdecl; - asm - // esi content has to be preserved with basm - push esi - xor edx,edx - xor eax,eax - mov esi,szStr - mov al,[esi] - xor cl,cl - @@lph_top: //only 4 of 9 instructions in here don't use AL, so optimal pipe use is impossible - xor edx,eax - inc esi - xor eax,eax - and cl,31 - mov al,[esi] - add cl,5 - test al,al - rol eax,cl //rol is u-pipe only, but pairable - //rol doesn't touch z-flag - jnz @@lph_top //5 clock tick loop. not bad. - xor eax,edx - pop esi - end; - - function CreateProcessUID(const pid: Cardinal): string; - var - pidrep: string[16]; - begin - str(pid, pidrep); - Result := Concat('mim.shlext.', pidrep, '$'); - end; - - function CreateUID: string; - var - pidrep, tidrep: string[16]; - begin - str(GetCurrentProcessId(), pidrep); - str(GetCurrentThreadId(), tidrep); - Result := Concat('mim.shlext.caller', pidrep, '$', tidrep); - end; - - // FPC doesn't support array[0..n] of Char extended syntax with Str() - - function wsprintf(lpOut, lpFmt: PChar; ArgInt: Integer): Integer; cdecl; external 'user32.dll' name 'wsprintfA'; - - procedure Str(i: Integer; S: PChar); - begin - i := wsprintf(s, '%d', i); - if i > 2 then PChar(s)[i] := #0; - end; - -{ IShlCom } - -type - - PLResult = ^LResult; - - // bare minimum interface of IDataObject, since GetData() is only required. - - PVTable_IDataObject = ^TVTable_IDataObject; - TVTable_IDataObject = record - { IUnknown } - QueryInterface: Pointer; - AddRef: function(Self: Pointer): Cardinal; stdcall; - Release: function(Self: Pointer): Cardinal; stdcall; - { IDataObject } - GetData: function(Self: Pointer; var formatetcIn: TFormatEtc; var medium: TStgMedium): HResult; stdcall; - GetDataHere: Pointer; - QueryGetData: Pointer; - GetCanonicalFormatEtc: Pointer; - SetData: Pointer; - EnumFormatEtc: Pointer; - DAdvise: Pointer; - DUnadvise: Pointer; - EnumDAdvise: Pointer; - end; - - PDataObject_Interface = ^TDataObject_Interface; - TDataObject_Interface = record - ptrVTable: PVTable_IDataObject; - end; - - { TShlComRec inherits from different interfaces with different function tables - all "compiler magic" is lost in this case, but it's pretty easy to return - a different function table for each interface, IContextMenu is returned - as IContextMenu'3' since it inherits from '2' and '1' } - - PVTable_IShellExtInit = ^TVTable_IShellExtInit; - TVTable_IShellExtInit = record - { IUnknown } - QueryInterface: Pointer; - AddRef: Pointer; - Release: Pointer; - { IShellExtInit } - Initialise: Pointer; - end; - - PShlComRec = ^TShlComRec; - PShellExtInit_Interface = ^TShellExtInit_Interface; - TShellExtInit_Interface = record - { pointer to function table } - ptrVTable: PVTable_IShellExtInit; - { instance data } - ptrInstance: PShlComRec; - { function table itself } - vTable: TVTable_IShellExtInit; - end; - - PVTable_IContextMenu3 = ^TVTable_IContextMenu3; - TVTable_IContextMenu3 = record - { IUnknown } - QueryInterface: Pointer; - AddRef: Pointer; - Release: Pointer; - { IContextMenu } - QueryContextMenu: Pointer; - InvokeCommand: Pointer; - GetCommandString: Pointer; - { IContextMenu2 } - HandleMenuMsg: Pointer; - { IContextMenu3 } - HandleMenuMsg2: Pointer; - end; - - PContextMenu3_Interface = ^TContextMenu3_Interface; - TContextMenu3_Interface = record - ptrVTable: PVTable_IContextMenu3; - ptrInstance: PShlComRec; - vTable: TVTable_IContextMenu3; - end; - - PCommon_Interface = ^TCommon_Interface; - TCommon_Interface = record - ptrVTable: Pointer; - ptrInstance: PShlComRec; - end; - - TShlComRec = record - ShellExtInit_Interface: TShellExtInit_Interface; - ContextMenu3_Interface: TContextMenu3_Interface; - {fields} - RefCount: LongInt; - // this is owned by the shell after items are added 'n' is used to - // grab menu information directly via id rather than array indexin' - hRootMenu: THandle; - idCmdFirst: Integer; - // most of the memory allocated is on this heap object so HeapDestroy() - // can do most of the cleanup, extremely lazy I know. - hDllHeap: THandle;
- // This is a submenu that recently used contacts are inserted into
- // the contact is inserted twice, once in its normal list (or group) and here
- // Note: These variables are global data, but refered to locally by each instance
- // Do not rely on these variables outside the process enumeration.
- hRecentMenu: THandle;
- RecentCount: Cardinal; // number of added items - // array of all the protocol icons, for every running instance! - ProtoIcons: ^TSlotProtoIconsArray; - ProtoIconsCount: Cardinal; - // maybe null, taken from IShellExtInit_Initalise() and AddRef()'d - // only used if a Miranda instance is actually running and a user - // is selected - pDataObject: PDataObject_Interface; - // DC is used for font metrics and saves on creating and destroying lots of DC handles - // during WM_MEASUREITEM - hMemDC: HDC; - end; - - { this is passed to the enumeration callback so it can process PID's with - main windows by the class name MIRANDANAME loaded with the plugin - and use the IPC stuff between enumerations -- } - - PEnumData = ^TEnumData; - TEnumData = record - Self: PShlComRec; - // autodetected, don't hard code since shells that don't support it - // won't send WM_MEASUREITETM/WM_DRAWITEM at all. - bOwnerDrawSupported: LongBool; - // as per user setting (maybe of multiple Mirandas) - bShouldOwnerDraw: LongBool; - idCmdFirst: Integer; - ipch: PHeaderIPC; - // OpenEvent()'d handle to give each IPC server an object to set signalled - hWaitFor: THandle; - pid: DWORD; // sub-unique value used to make work object name - end; - - procedure FreeGroupTreeAndEmptyGroups(hParentMenu: THandle; pp, p: PGroupNode); - var - q: PGroupNode; - begin - while p <> nil do - begin - q := p^.Right; - if p^.Left <> nil then - begin - FreeGroupTreeAndEmptyGroups(p^.Left^.hMenu, p, p^.Left); - end; //if - if p^.dwItems = 0 then - begin - if pp <> nil then - begin - DeleteMenu(pp^.hMenu, p^.hMenuGroupID, MF_BYCOMMAND) - end else begin - DeleteMenu(hParentMenu, p^.hMenuGroupID, MF_BYCOMMAND); - end; //if - end else begin - // make sure this node's parent know's it exists - if pp <> nil then inc (pp^.dwItems); - end; - Dispose(p); - p := q; - end; - end; - - procedure DecideMenuItemInfo(pct: PSlotIPC; pg: PGroupNode; var mii: TMenuItemInfo; lParam: PEnumData); - var - psd: PMenuDrawInfo; - hDllHeap: THandle; - j, c: Cardinal; - pp: ^TSlotProtoIconsArray; - begin - mii.wID := lParam^.idCmdFirst; - Inc(lParam^.idCmdFirst); - // get the heap object - hDllHeap := lParam^.Self^.hDllHeap; - psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo)); - if pct <> nil then - begin - psd^.cch := pct^.cbStrSection-1; // no null; - psd^.szText := HeapAlloc(hDllHeap, 0, pct^.cbStrSection); - strcpy(psd^.szText, PChar(Integer(pct)+sizeof(TSlotIPC))); - psd^.hContact := pct^.hContact; - psd^.fTypes := [dtContact]; - // find the protocol icon array to use and which status - c := lParam^.Self^.protoIconsCount; - pp := lParam^.Self^.protoIcons; - psd^.hStatusIcon := 0; - while c > 0 do - begin - dec(c); - if (pp[c].hProto = pct^.hProto) and (pp[c].pid = lParam^.pid) then - begin - psd^.hStatusIcon := pp[c].hIcons[pct^.Status - ID_STATUS_OFFLINE]; - psd^.hStatusBitmap := pp[c].hBitmaps[pct^.Status - ID_STATUS_OFFLINE]; - break; - end; - end; //while - psd^.pid := lParam^.pid; - end else if pg <> nil then - begin - // store the given ID - pg^.hMenuGroupID := mii.wID; - // steal the pointer from the group node it should be on the heap - psd^.cch := pg^.cchGroup; - psd^.szText := pg^.szGroup; - psd^.fTypes := [dtGroup]; - end; //if - psd^.wID := mii.wID; - psd^.szProfile := nil; - // store - mii.dwItemData := Integer(psd); - - if ((lParam^.bOwnerDrawSupported) and (lParam^.bShouldOwnerDraw)) then - begin - mii.fType := MFT_OWNERDRAW; - Pointer(mii.dwTypeData) := psd; - end else begin - // normal menu - mii.fType := MFT_STRING; - if pct <> nil then - begin - Integer(mii.dwTypeData) := Integer(pct) + sizeof(TSlotIPC); - end else begin - mii.dwTypeData := pg^.szGroup; - end; - { For Vista + let the system draw the theme and icons, pct = contact associated data } - if VistaOrLater and ( pct <> nil ) and ( psd <> nil) then - begin - mii.fMask := MIIM_BITMAP or MIIM_FTYPE or MIIM_ID or MIIM_DATA or MIIM_STRING; - // BuildSkinIcons() built an array of bitmaps which we can use here - mii.hBmpItem := psd^.hStatusBitmap; - end; - end; //if +
+{$IFDEF FPC}
+{$PACKRECORDS 4}
+{$MODE Delphi}
+{$ENDIF}
+
+interface
+
+uses
+
+ Windows, m_api, shlipc, shlicons;
+
+{$DEFINE COM_STRUCTS}
+{$DEFINE SHLCOM}
+{$INCLUDE shlc.inc}
+{$UNDEF SHLCOM}
+{$UNDEF COM_STRUCTS}
+function DllGetClassObject(const CLSID: TCLSID; const IID: TIID; var Obj): HResult; stdcall;
+function DllCanUnloadNow: HResult; stdcall;
+
+procedure InvokeThreadServer;
+
+procedure CheckRegisterServer;
+
+procedure CheckUnregisterServer;
+
+function RemoveCOMRegistryEntries: HResult;
+
+function ExtractIcon(hInst: THandle; pszExe: PChar; nIndex: Integer): HICON; stdcall;
+ external 'shell32.dll' name 'ExtractIconA';
+
+implementation
+
+var
+
+ dllpublic: record FactoryCount: Integer;
+ ObjectCount: Integer;
+end;
+
+VistaOrLater:
+Boolean;
+
+{$DEFINE COMAPI}
+{$INCLUDE shlc.inc}
+{$UNDEF COMAPI}
+
+const
+
+ IPC_PACKET_SIZE = $1000 * 32;
+ // IPC_PACKET_NAME = 'm.mi.miranda.ipc'; // prior to 1.0.6.6
+ // IPC_PACKET_NAME = 'mi.miranda.IPCServer'; // prior to 2.0.0.9
+ IPC_PACKET_NAME = 'm.mi.miranda.ipc.server';
+
+const
+
+ { Flags returned by IContextMenu*:QueryContextMenu() }
+
+ CMF_NORMAL = $00000000;
+ CMF_DEFAULTONLY = $00000001;
+ CMF_VERBSONLY = $00000002;
+ CMF_EXPLORE = $00000004;
+ CMF_NOVERBS = $00000008;
+ CMF_CANRENAME = $00000010;
+ CMF_NODEFAULT = $00000020;
+ CMF_INCLUDESTATIC = $00000040;
+ CMF_RESERVED = $FFFF0000; { view specific }
+
+ { IContextMenu*:GetCommandString() uType flags }
+
+ GCS_VERBA = $00000000; // canonical verb
+ GCS_HELPTEXTA = $00000001; // help text (for status bar)
+ GCS_VALIDATEA = $00000002; // validate command exists
+ GCS_VERBW = $00000004; // canonical verb (unicode)
+ GC_HELPTEXTW = $00000005; // help text (unicode version)
+ GCS_VALIDATEW = $00000006; // validate command exists (unicode)
+ GCS_UNICODE = $00000004; // for bit testing - Unicode string
+ GCS_VERB = GCS_VERBA; //
+ GCS_HELPTEXT = GCS_HELPTEXTA;
+ GCS_VALIDATE = GCS_VALIDATEA;
+
+type
+
+ { this structure is returned by InvokeCommand() }
+
+ PCMInvokeCommandInfo = ^TCMInvokeCommandInfo;
+
+ TCMInvokeCommandInfo = packed record
+ cbSize: DWORD;
+ fMask: DWORD;
+ hwnd: hwnd;
+ lpVerb: PChar; { maybe index, type cast as Integer }
+ lpParams: PChar;
+ lpDir: PChar;
+ nShow: Integer;
+ dwHotkey: DWORD;
+ HICON: THandle;
+ end;
+
+ { completely stolen from modules.c: 'NameHashFunction' modified slightly }
+
+function StrHash(const szStr: PChar): DWORD; cdecl;
+asm
+ // esi content has to be preserved with basm
+ push esi
+ xor edx,edx
+ xor eax,eax
+ mov esi,szStr
+ mov al,[esi]
+ xor cl,cl
+@@lph_top: // only 4 of 9 instructions in here don't use AL, so optimal pipe use is impossible
+ xor edx,eax
+ inc esi
+ xor eax,eax
+ and cl,31
+ mov al,[esi]
+ add cl,5
+ test al,al
+ rol eax,cl // rol is u-pipe only, but pairable
+ // rol doesn't touch z-flag
+ jnz @@lph_top // 5 clock tick loop. not bad.
+ xor eax,edx
+ pop esi
+end;
+
+function CreateProcessUID(const pid: Cardinal): string;
+var
+ pidrep: string[16];
+begin
+ str(pid, pidrep);
+ Result := Concat('mim.shlext.', pidrep, '$');
+end;
+
+function CreateUID: string;
+var
+ pidrep, tidrep: string[16];
+begin
+ str(GetCurrentProcessId(), pidrep);
+ str(GetCurrentThreadId(), tidrep);
+ Result := Concat('mim.shlext.caller', pidrep, '$', tidrep);
+end;
+
+// FPC doesn't support array[0..n] of Char extended syntax with Str()
+
+function wsprintf(lpOut, lpFmt: PChar; ArgInt: Integer): Integer; cdecl;
+ external 'user32.dll' name 'wsprintfA';
+
+procedure str(i: Integer; S: PChar);
+begin
+ i := wsprintf(S, '%d', i);
+ if i > 2 then
+ PChar(S)[i] := #0;
+end;
+
+{ IShlCom }
+
+type
+
+ PLResult = ^LResult;
+
+ // bare minimum interface of IDataObject, since GetData() is only required.
+
+ PVTable_IDataObject = ^TVTable_IDataObject;
+
+ TVTable_IDataObject = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: function(Self: Pointer): Cardinal; stdcall;
+ Release: function(Self: Pointer): Cardinal; stdcall;
+ { IDataObject }
+ GetData: function(Self: Pointer; var formatetcIn: TFormatEtc; var medium: TStgMedium)
+ : HResult; stdcall;
+ GetDataHere: Pointer;
+ QueryGetData: Pointer;
+ GetCanonicalFormatEtc: Pointer;
+ SetData: Pointer;
+ EnumFormatEtc: Pointer;
+ DAdvise: Pointer;
+ DUnadvise: Pointer;
+ EnumDAdvise: Pointer;
+ end;
+
+ PDataObject_Interface = ^TDataObject_Interface;
+
+ TDataObject_Interface = record
+ ptrVTable: PVTable_IDataObject;
+ end;
+
+ { TShlComRec inherits from different interfaces with different function tables
+ all "compiler magic" is lost in this case, but it's pretty easy to return
+ a different function table for each interface, IContextMenu is returned
+ as IContextMenu'3' since it inherits from '2' and '1' }
+
+ PVTable_IShellExtInit = ^TVTable_IShellExtInit;
+
+ TVTable_IShellExtInit = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: Pointer;
+ Release: Pointer;
+ { IShellExtInit }
+ Initialise: Pointer;
+ end;
+
+ PShlComRec = ^TShlComRec;
+ PShellExtInit_Interface = ^TShellExtInit_Interface;
+
+ TShellExtInit_Interface = record
+ { pointer to function table }
+ ptrVTable: PVTable_IShellExtInit;
+ { instance data }
+ ptrInstance: PShlComRec;
+ { function table itself }
+ vTable: TVTable_IShellExtInit;
+ end;
+
+ PVTable_IContextMenu3 = ^TVTable_IContextMenu3;
+
+ TVTable_IContextMenu3 = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: Pointer;
+ Release: Pointer;
+ { IContextMenu }
+ QueryContextMenu: Pointer;
+ InvokeCommand: Pointer;
+ GetCommandString: Pointer;
+ { IContextMenu2 }
+ HandleMenuMsg: Pointer;
+ { IContextMenu3 }
+ HandleMenuMsg2: Pointer;
+ end;
+
+ PContextMenu3_Interface = ^TContextMenu3_Interface;
+
+ TContextMenu3_Interface = record
+ ptrVTable: PVTable_IContextMenu3;
+ ptrInstance: PShlComRec;
+ vTable: TVTable_IContextMenu3;
+ end;
+
+ PCommon_Interface = ^TCommon_Interface;
+
+ TCommon_Interface = record
+ ptrVTable: Pointer;
+ ptrInstance: PShlComRec;
+ end;
+
+ TShlComRec = record
+ ShellExtInit_Interface: TShellExtInit_Interface;
+ ContextMenu3_Interface: TContextMenu3_Interface;
+ { fields }
+ RefCount: LongInt;
+ // this is owned by the shell after items are added 'n' is used to
+ // grab menu information directly via id rather than array indexin'
+ hRootMenu: THandle;
+ idCmdFirst: Integer;
+ // most of the memory allocated is on this heap object so HeapDestroy()
+ // can do most of the cleanup, extremely lazy I know.
+ hDllHeap: THandle;
+ // This is a submenu that recently used contacts are inserted into
+ // the contact is inserted twice, once in its normal list (or group) and here
+ // Note: These variables are global data, but refered to locally by each instance
+ // Do not rely on these variables outside the process enumeration.
+ hRecentMenu: THandle;
+ RecentCount: Cardinal; // number of added items
+ // array of all the protocol icons, for every running instance!
+ ProtoIcons: ^TSlotProtoIconsArray;
+ ProtoIconsCount: Cardinal;
+ // maybe null, taken from IShellExtInit_Initalise() and AddRef()'d
+ // only used if a Miranda instance is actually running and a user
+ // is selected
+ pDataObject: PDataObject_Interface;
+ // DC is used for font metrics and saves on creating and destroying lots of DC handles
+ // during WM_MEASUREITEM
+ hMemDC: HDC;
+ end;
+
+ { this is passed to the enumeration callback so it can process PID's with
+ main windows by the class name MIRANDANAME loaded with the plugin
+ and use the IPC stuff between enumerations -- }
+
+ PEnumData = ^TEnumData;
+
+ TEnumData = record
+ Self: PShlComRec;
+ // autodetected, don't hard code since shells that don't support it
+ // won't send WM_MEASUREITETM/WM_DRAWITEM at all.
+ bOwnerDrawSupported: LongBool;
+ // as per user setting (maybe of multiple Mirandas)
+ bShouldOwnerDraw: LongBool;
+ idCmdFirst: Integer;
+ ipch: PHeaderIPC;
+ // OpenEvent()'d handle to give each IPC server an object to set signalled
+ hWaitFor: THandle;
+ pid: DWORD; // sub-unique value used to make work object name
+ end;
+
+procedure FreeGroupTreeAndEmptyGroups(hParentMenu: THandle; pp, p: PGroupNode);
+var
+ q: PGroupNode;
+begin
+ while p <> nil do
+ begin
+ q := p^.Right;
+ if p^.Left <> nil then
+ begin
+ FreeGroupTreeAndEmptyGroups(p^.Left^.hMenu, p, p^.Left);
+ end; // if
+ if p^.dwItems = 0 then
+ begin
+ if pp <> nil then
+ begin
+ DeleteMenu(pp^.hMenu, p^.hMenuGroupID, MF_BYCOMMAND)
+ end
+ else
+ begin
+ DeleteMenu(hParentMenu, p^.hMenuGroupID, MF_BYCOMMAND);
+ end; // if
+ end
+ else
+ begin
+ // make sure this node's parent know's it exists
+ if pp <> nil then
+ inc(pp^.dwItems);
+ end;
+ Dispose(p);
+ p := q;
+ end;
+end;
+
+procedure DecideMenuItemInfo(pct: PSlotIPC; pg: PGroupNode; var mii: TMenuItemInfo;
+ lParam: PEnumData);
+var
+ psd: PMenuDrawInfo;
+ hDllHeap: THandle;
+ j, c: Cardinal;
+ pp: ^TSlotProtoIconsArray;
+begin
+ mii.wID := lParam^.idCmdFirst;
+ inc(lParam^.idCmdFirst);
+ // get the heap object
+ hDllHeap := lParam^.Self^.hDllHeap;
+ psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
+ if pct <> nil then
+ begin
+ psd^.cch := pct^.cbStrSection - 1; // no null;
+ psd^.szText := HeapAlloc(hDllHeap, 0, pct^.cbStrSection);
+ lstrcpya(psd^.szText, PChar(Integer(pct) + sizeof(TSlotIPC)));
+ psd^.hContact := pct^.hContact;
+ psd^.fTypes := [dtContact];
+ // find the protocol icon array to use and which status
+ c := lParam^.Self^.ProtoIconsCount;
+ pp := lParam^.Self^.ProtoIcons;
+ psd^.hStatusIcon := 0;
+ while c > 0 do
+ begin
+ dec(c);
+ if (pp[c].hProto = pct^.hProto) and (pp[c].pid = lParam^.pid) then
+ begin
+ psd^.hStatusIcon := pp[c].hIcons[pct^.Status - ID_STATUS_OFFLINE];
+ psd^.hStatusBitmap := pp[c].hBitmaps[pct^.Status - ID_STATUS_OFFLINE];
+ break;
+ end;
+ end; // while
+ psd^.pid := lParam^.pid;
+ end
+ else if pg <> nil then
+ begin
+ // store the given ID
+ pg^.hMenuGroupID := mii.wID;
+ // steal the pointer from the group node it should be on the heap
+ psd^.cch := pg^.cchGroup;
+ psd^.szText := pg^.szGroup;
+ psd^.fTypes := [dtGroup];
+ end; // if
+ psd^.wID := mii.wID;
+ psd^.szProfile := nil;
+ // store
+ mii.dwItemData := Integer(psd);
+
+ if ((lParam^.bOwnerDrawSupported) and (lParam^.bShouldOwnerDraw)) then
+ begin
+ mii.fType := MFT_OWNERDRAW;
+ Pointer(mii.dwTypeData) := psd;
+ end
+ else
+ begin
+ // normal menu
+ mii.fType := MFT_STRING;
+ if pct <> nil then
+ begin
+ int_ptr(mii.dwTypeData) := int_ptr(pct) + sizeof(TSlotIPC);
+ end
+ else
+ begin
+ mii.dwTypeData := pg^.szGroup;
+ end;
+ { For Vista + let the system draw the theme and icons, pct = contact associated data }
+ if VistaOrLater and (pct <> nil) and (psd <> nil) then
+ begin
+ mii.fMask := MIIM_BITMAP or MIIM_FTYPE or MIIM_ID or MIIM_DATA or MIIM_STRING;
+ // BuildSkinIcons() built an array of bitmaps which we can use here
+ mii.hBmpItem := psd^.hStatusBitmap;
+ end;
+ end; // if
+end;
+
+// must be called after DecideMenuItemInfo()
+procedure BuildMRU(pct: PSlotIPC; var mii: TMenuItemInfo; lParam: PEnumData);
+begin
+ if pct^.MRU > 0 then
+ begin
+ inc(lParam^.Self^.RecentCount);
+ // lParam^.Self == pointer to object data
+ InsertMenuitem(lParam^.Self^.hRecentMenu, $FFFFFFFF, True, mii);
+ end;
+end;
+
+procedure BuildContactTree(group: PGroupNode; lParam: PEnumData);
+label
+ grouploop;
+var
+ pct: PSlotIPC;
+ pg, px: PGroupNode;
+ str: TStrTokRec;
+ sz: PChar;
+ Hash: Cardinal;
+ Depth: Cardinal;
+ mii: TMenuItemInfo;
+begin
+ // set up the menu item
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_TYPE or MIIM_DATA;
+ // set up the scanner
+ str.szSet := ['\'];
+ str.bSetTerminator := False;
+ // go thru all the contacts
+ pct := lParam^.ipch^.ContactsBegin;
+ while (pct <> nil) and (pct^.cbSize = sizeof(TSlotIPC)) and (pct^.fType = REQUEST_CONTACTS) do
+ begin
+ if pct^.hGroup <> 0 then
+ begin
+ // at the end of the slot header is the contact's display name
+ // and after a double NULL char there is the group string, which has the full path of the group
+ // this must be tokenised at '\' and we must walk the in memory group tree til we find our group
+ // this is faster than the old version since we only ever walk one or at most two levels of the tree
+ // per tokenised section, and it doesn't matter if two levels use the same group name (which is valid)
+ // as the tokens processed is equatable to depth of the tree
+ str.szStr := PChar(Integer(pct) + sizeof(TSlotIPC) + pct^.cbStrSection + 1);
+ sz := StrTok(str);
+ // restore the root
+ pg := group;
+ Depth := 0;
+ while sz <> nil do
+ begin
+ Hash := StrHash(sz);
+ // find this node within
+ while pg <> nil do
+ begin
+ // does this node have the right hash and the right depth?
+ if (Hash = pg^.Hash) and (Depth = pg^.Depth) then
+ break;
+ // each node may have a left pointer going to a sub tree
+ // the path syntax doesn't know if a group is a group at the same level
+ // or a nested one, which means the search node can be anywhere
+ px := pg^.Left;
+ if px <> nil then
+ begin
+ // keep searching this level
+ while px <> nil do
+ begin
+ if (Hash = px^.Hash) and (Depth = px^.Depth) then
+ begin
+ // found the node we're looking for at the next level to pg, px is now pq for next time
+ pg := px;
+ goto grouploop;
+ end; // if
+ px := px^.Right;
+ end; // if
+ end; // if
+ pg := pg^.Right;
+ end; // while
+ grouploop:
+ inc(Depth);
+ // process next token
+ sz := StrTok(str);
+ end; // while
+ // tokenisation finished, if pg <> nil then the group is found
+ if pg <> nil then
+ begin
+ DecideMenuItemInfo(pct, nil, mii, lParam);
+ BuildMRU(pct, mii, lParam);
+ InsertMenuitem(pg^.hMenu, $FFFFFFFF, True, mii);
+ inc(pg^.dwItems);
+ end;
+ end; // if
+ pct := pct^.Next;
+ end; // while
+end;
+
+procedure BuildMenuGroupTree(p: PGroupNode; lParam: PEnumData; hLastMenu: hMenu);
+var
+ mii: TMenuItemInfo;
+begin
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_DATA or MIIM_TYPE or MIIM_SUBMENU;
+ // go thru each group and create a menu for it adding submenus too.
+ while p <> nil do
+ begin
+ mii.hSubMenu := CreatePopupMenu();
+ if p^.Left <> nil then
+ BuildMenuGroupTree(p^.Left, lParam, mii.hSubMenu);
+ p^.hMenu := mii.hSubMenu;
+ DecideMenuItemInfo(nil, p, mii, lParam);
+ InsertMenuitem(hLastMenu, $FFFFFFFF, True, mii);
+ p := p^.Right;
+ end; // while
+end;
+
+{ this callback is triggered by the menu code and IPC is already taking place,
+ just the transfer type+data needs to be setup }
+function ClearMRUIPC(pipch: PHeaderIPC; // IPC header info, already mapped
+ hWorkThreadEvent: THandle; // event object being waited on on miranda thread
+ hAckEvent: THandle; // ack event object that has been created
+ psd: PMenuDrawInfo // command/draw info
+ ): Integer; stdcall;
+begin
+ Result := S_OK;
+ ipcPrepareRequests(IPC_PACKET_SIZE, pipch, REQUEST_CLEARMRU);
+ ipcSendRequest(hWorkThreadEvent, hAckEvent, pipch, 100);
+end;
+
+procedure RemoveCheckmarkSpace(hMenu: hMenu);
+const
+ MIM_STYLE = $00000010;
+ MNS_CHECKORBMP = $4000000;
+type
+ TMENUINFO = record
+ cbSize: DWORD;
+ fMask: DWORD;
+ dwStyle: DWORD;
+ cyMax: LongInt;
+ hbrBack: THandle;
+ dwContextHelpID: DWORD;
+ dwMenuData: Pointer;
+ end;
+var
+ SetMenuInfo: function(hMenu: hMenu; var mi: TMENUINFO): Boolean; stdcall;
+ mi: TMENUINFO;
+begin
+ if not VistaOrLater then
+ Exit;
+ SetMenuInfo := GetProcAddress(GetModuleHandle('user32'), 'SetMenuInfo');
+ if @SetMenuInfo = nil then
+ Exit;
+ mi.cbSize := sizeof(mi);
+ mi.fMask := MIM_STYLE;
+ mi.dwStyle := MNS_CHECKORBMP;
+ SetMenuInfo(hMenu, mi);
+end;
+
+procedure BuildMenus(lParam: PEnumData);
+{$DEFINE SHL_IDC}
+{$DEFINE SHL_KEYS}
+{$INCLUDE shlc.inc}
+{$UNDEF SHL_KEYS}
+{$UNDEF SHL_IDC}
+var
+ hBaseMenu: hMenu;
+ hGroupMenu: hMenu;
+ pg: PSlotIPC;
+ szProf: PChar;
+ mii: TMenuItemInfo;
+ j: TGroupNodeList;
+ p, q: PGroupNode;
+ Depth, Hash: Cardinal;
+ Token: PChar;
+ tk: TStrTokRec;
+ szBuf: PChar;
+ hDllHeap: THandle;
+ psd: PMenuDrawInfo;
+ c: Cardinal;
+ pp: ^TSlotProtoIconsArray;
+begin
+ ZeroMemory(@mii, sizeof(mii));
+ hDllHeap := lParam^.Self^.hDllHeap;
+ hBaseMenu := lParam^.Self^.hRootMenu;
+ // build an in memory tree of the groups
+ pg := lParam^.ipch^.GroupsBegin;
+ tk.szSet := ['\'];
+ tk.bSetTerminator := False;
+ j.First := nil;
+ j.Last := nil;
+ while pg <> nil do
+ begin
+ if (pg^.cbSize <> sizeof(TSlotIPC)) or (pg^.fType <> REQUEST_GROUPS) then
+ break;
+ Depth := 0;
+ p := j.First; // start at root again
+ // get the group
+ int_ptr(tk.szStr) := (int_ptr(pg) + sizeof(TSlotIPC));
+ // find each word between \ and create sub groups if needed.
+ Token := StrTok(tk);
+ while Token <> nil do
+ begin
+ Hash := StrHash(Token);
+ // if the (sub)group doesn't exist, create it.
+ q := FindGroupNode(p, Hash, Depth);
+ if q = nil then
+ begin
+ q := AllocGroupNode(@j, p, Depth);
+ q^.Depth := Depth;
+ // this is the hash of this group node, but it can be anywhere
+ // i.e. Foo\Foo this is because each node has a different depth
+ // trouble is contacts don't come with depths!
+ q^.Hash := Hash;
+ // don't assume that pg^.hGroup's hash is valid for this token
+ // since it maybe Miranda\Blah\Blah and we have created the first node
+ // which maybe Miranda, thus giving the wrong hash
+ // since "Miranda" can be a group of it's own and a full path
+ q^.cchGroup := lstrlena(Token);
+ q^.szGroup := HeapAlloc(hDllHeap, 0, q^.cchGroup + 1);
+ lstrcpya(q^.szGroup, Token);
+ q^.dwItems := 0;
+ end;
+ p := q;
+ inc(Depth);
+ Token := StrTok(tk);
+ end; // while
+ pg := pg^.Next;
+ end; // while
+ // build the menus inserting into hGroupMenu which will be a submenu of
+ // the instance menu item. e.g. Miranda -> [Groups ->] contacts
+ hGroupMenu := CreatePopupMenu();
+
+ // allocate MRU menu, this will be associated with the higher up menu
+ // so doesn't need to be freed (unless theres no MRUs items attached)
+ // This menu is per process but the handle is stored globally (like a stack)
+ lParam^.Self^.hRecentMenu := CreatePopupMenu();
+ lParam^.Self^.RecentCount := 0;
+ // create group menus only if they exist!
+ if lParam^.ipch^.GroupsBegin <> nil then
+ begin
+ BuildMenuGroupTree(j.First, lParam, hGroupMenu);
+ // add contacts that have a group somewhere
+ BuildContactTree(j.First, lParam);
+ end;
+ //
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_TYPE or MIIM_DATA;
+ // add all the contacts that have no group (which maybe all of them)
+ pg := lParam^.ipch^.ContactsBegin;
+ while pg <> nil do
+ begin
+ if (pg^.cbSize <> sizeof(TSlotIPC)) or (pg^.fType <> REQUEST_CONTACTS) then
+ break;
+ if pg^.hGroup = 0 then
+ begin
+ DecideMenuItemInfo(pg, nil, mii, lParam);
+ BuildMRU(pg, mii, lParam);
+ InsertMenuitem(hGroupMenu, $FFFFFFFF, True, mii);
+ end; // if
+ pg := pg^.Next;
+ end; // while
+
+ // insert MRU menu as a submenu of the contact menu only if
+ // the MRU list has been created, the menu popup will be deleted by itself
+ if lParam^.Self^.RecentCount > 0 then
+ begin
+
+ // insert seperator and 'clear list' menu
+ mii.fType := MFT_SEPARATOR;
+ mii.fMask := MIIM_TYPE;
+ InsertMenuitem(lParam^.Self^.hRecentMenu, $FFFFFFFF, True, mii);
+
+ // insert 'clear MRU' item and setup callback
+ mii.fMask := MIIM_TYPE or MIIM_ID or MIIM_DATA;
+ mii.wID := lParam^.idCmdFirst;
+ inc(lParam^.idCmdFirst);
+ mii.fType := MFT_STRING;
+ mii.dwTypeData := lParam^.ipch^.ClearEntries; // "Clear entries"
+ // allocate menu substructure
+ psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
+ psd^.fTypes := [dtCommand];
+ psd^.MenuCommandCallback := @ClearMRUIPC;
+ psd^.wID := mii.wID;
+ // this is needed because there is a clear list command per each process.
+ psd^.pid := lParam^.pid;
+ mii.dwItemData := Integer(psd);
+ InsertMenuitem(lParam^.Self^.hRecentMenu, $FFFFFFFF, True, mii);
+
+ // insert MRU submenu into group menu (with) ownerdraw support as needed
+ psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
+ psd^.szProfile := 'MRU';
+ psd^.fTypes := [dtGroup];
+ // the IPC string pointer wont be around forever, must make a copy
+ psd^.cch := strlen(lParam^.ipch^.MRUMenuName);
+ psd^.szText := HeapAlloc(hDllHeap, 0, psd^.cch + 1);
+ lstrcpyn(psd^.szText, lParam^.ipch^.MRUMenuName, sizeof(lParam^.ipch^.MRUMenuName) - 1);
+
+ mii.dwItemData := Integer(psd);
+ if (lParam^.bOwnerDrawSupported) and (lParam^.bShouldOwnerDraw) then
+ begin
+ mii.fType := MFT_OWNERDRAW;
+ Pointer(mii.dwTypeData) := psd;
+ end
+ else
+ begin
+ mii.dwTypeData := lParam^.ipch^.MRUMenuName; // 'Recent';
end;
-
- // must be called after DecideMenuItemInfo()
- procedure BuildMRU(pct: PSlotIPC; var mii: TMenuItemInfo; lParam: PEnumData);
- begin
- if pct^.MRU > 0 then
- begin
- Inc(lParam^.Self^.RecentCount);
- // lParam^.Self == pointer to object data
- InsertMenuitem(lParam^.Self^.hRecentMenu, $FFFFFFFF, True, mii);
- end;
- end;
- - procedure BuildContactTree(group: PGroupNode; lParam: PEnumData); - label - grouploop; - var - pct: PSlotIPC; - pg, px: PGroupNode; - str: TStrTokRec; - sz: PChar; - Hash: Cardinal; - Depth: Cardinal; - mii: TMenuItemInfo; - begin - // set up the menu item - mii.cbSize := sizeof(TMenuItemInfo); - mii.fMask := MIIM_ID or MIIM_TYPE or MIIM_DATA; - // set up the scanner - str.szSet := ['\']; - str.bSetTerminator := False; - // go thru all the contacts - pct := lParam^.ipch^.ContactsBegin; - while (pct <> nil) and (pct^.cbSize=sizeof(TSlotIPC)) and (pct^.fType=REQUEST_CONTACTS) do - begin - if pct^.hGroup <> 0 then - begin - // at the end of the slot header is the contact's display name - // and after a double NULL char there is the group string, which has the full path of the group - // this must be tokenised at '\' and we must walk the in memory group tree til we find our group - // this is faster than the old version since we only ever walk one or at most two levels of the tree - // per tokenised section, and it doesn't matter if two levels use the same group name (which is valid) - // as the tokens processed is equatable to depth of the tree - str.szStr := PChar(Integer(pct)+sizeof(TSlotIPC)+pct^.cbStrSection+1); - sz := StrTok(str); - // restore the root - pg := group; - Depth := 0; - while sz <> nil do - begin - Hash := StrHash(sz); - // find this node within - while pg <> nil do - begin - // does this node have the right hash and the right depth? - if (Hash = pg^.Hash) and (Depth = pg^.Depth) then Break; - // each node may have a left pointer going to a sub tree - // the path syntax doesn't know if a group is a group at the same level - // or a nested one, which means the search node can be anywhere - px := pg^.Left; - if px <> nil then - begin - // keep searching this level - while px <> nil do - begin - if (hash = px^.Hash) and (Depth = px^.Depth) then - begin - // found the node we're looking for at the next level to pg, px is now pq for next time - pg := px; - goto grouploop; - end; //if - px := px^.Right; - end; //if - end; //if - pg := pg^.Right; - end; //while - grouploop: - inc (Depth); - // process next token - sz := StrTok(str); - end; //while - // tokenisation finished, if pg <> nil then the group is found - if pg <> nil then - begin - DecideMenuItemInfo(pct, nil, mii, lParam);
- BuildMRU(pct, mii, lParam); - InsertMenuitem(pg^.hMenu, $FFFFFFFF,True, mii); - Inc(pg^.dwItems); - end; - end; //if - pct := pct^.Next; - end; //while - end; - - procedure BuildMenuGroupTree(p: PGroupNode; lParam: PEnumData; hLastMenu: HMENU); - var - mii: TMenuItemInfo; - begin - mii.cbSize := sizeof(TMenuItemInfo); - mii.fMask := MIIM_ID or MIIM_DATA or MIIM_TYPE or MIIM_SUBMENU; - // go thru each group and create a menu for it adding submenus too. - while p <> nil do - begin - mii.hSubMenu := CreatePopupMenu(); - if p^.Left <> nil then BuildMenuGroupTree(p^.Left, lParam, mii.hSubMenu); - p^.hMenu := mii.hSubMenu; - DecideMenuItemInfo(nil, P, mii, lParam); - InsertMenuItem(hLastMenu, $FFFFFFFF, True, mii); - p := p^.Right; - end; //while + mii.wID := lParam^.idCmdFirst;
+ inc(lParam^.idCmdFirst);
+ mii.fMask := MIIM_TYPE or MIIM_SUBMENU or MIIM_DATA or MIIM_ID;
+ mii.hSubMenu := lParam^.Self^.hRecentMenu;
+ InsertMenuitem(hGroupMenu, 0, True, mii);
+ end
+ else
+ begin
+ // no items were attached to the MRU, delete the MRU menu
+ DestroyMenu(lParam^.Self^.hRecentMenu);
+ lParam^.Self^.hRecentMenu := 0;
+ end;
+
+ // allocate display info/memory for "Miranda" string
+
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_DATA or MIIM_TYPE or MIIM_SUBMENU;
+ if VistaOrLater then
+ begin
+ mii.fMask := MIIM_ID or MIIM_DATA or MIIM_FTYPE or MIIM_SUBMENU or MIIM_STRING or
+ MIIM_BITMAP;
+ end;
+ mii.hSubMenu := hGroupMenu;
+
+ // by default, the menu will have space for icons and checkmarks (on Vista+) and we don't need this
+ RemoveCheckmarkSpace(hGroupMenu);
+
+ psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
+ psd^.cch := strlen(lParam^.ipch^.MirandaName);
+ psd^.szText := HeapAlloc(hDllHeap, 0, psd^.cch + 1);
+ lstrcpyn(psd^.szText, lParam^.ipch^.MirandaName, sizeof(lParam^.ipch^.MirandaName) - 1);
+ // there may not be a profile name
+ pg := lParam^.ipch^.DataPtr;
+ psd^.szProfile := nil;
+ if ((pg <> nil) and (pg^.Status = STATUS_PROFILENAME)) then
+ begin
+ psd^.szProfile := HeapAlloc(hDllHeap, 0, pg^.cbStrSection);
+ lstrcpya(psd^.szProfile, PChar(Integer(pg) + sizeof(TSlotIPC)));
+ end; // if
+ // owner draw menus need ID's
+ mii.wID := lParam^.idCmdFirst;
+ inc(lParam^.idCmdFirst);
+ psd^.fTypes := [dtEntry];
+ psd^.wID := mii.wID;
+ psd^.hContact := 0;
+ // get Miranda's icon or bitmap
+ c := lParam^.Self^.ProtoIconsCount;
+ pp := lParam^.Self^.ProtoIcons;
+ while c > 0 do
+ begin
+ dec(c);
+ if (pp[c].pid = lParam^.pid) and (pp[c].hProto = 0) then
+ begin
+ // either of these can be 0
+ psd^.hStatusIcon := pp[c].hIcons[0];
+ mii.hBmpItem := pp[c].hBitmaps[0];
+ break;
+ end; // if
+ end; // while
+ mii.dwItemData := Integer(psd);
+ if ((lParam^.bOwnerDrawSupported) and (lParam^.bShouldOwnerDraw)) then
+ begin
+ mii.fType := MFT_OWNERDRAW;
+ Pointer(mii.dwTypeData) := psd;
+ end
+ else
+ begin
+ mii.fType := MFT_STRING;
+ mii.dwTypeData := lParam^.ipch^.MirandaName;
+ mii.cch := sizeof(lParam^.ipch^.MirandaName) - 1;
+ end;
+ // add it all
+ InsertMenuitem(hBaseMenu, 0, True, mii);
+ // free the group tree
+ FreeGroupTreeAndEmptyGroups(hGroupMenu, nil, j.First);
+end;
+
+procedure BuildSkinIcons(lParam: PEnumData);
+var
+ pct: PSlotIPC;
+ p, d: PSlotProtoIcons;
+ Self: PShlComRec;
+ j: Cardinal;
+ imageFactory: PImageFactory_Interface;
+begin
+ pct := lParam^.ipch^.NewIconsBegin;
+ Self := lParam^.Self;
+ while (pct <> nil) do
+ begin
+ if (pct^.cbSize <> sizeof(TSlotIPC)) or (pct^.fType <> REQUEST_NEWICONS) then
+ break;
+ int_ptr(p) := int_ptr(pct) + sizeof(TSlotIPC);
+ ReAllocMem(Self^.ProtoIcons, (Self^.ProtoIconsCount + 1) * sizeof(TSlotProtoIcons));
+ d := @Self^.ProtoIcons[Self^.ProtoIconsCount];
+ CopyMemory(d, p, sizeof(TSlotProtoIcons));
+
+ {
+ If using Vista (or later), clone all the icons into bitmaps and keep these around,
+ if using anything older, just use the default code, the bitmaps (and or icons) will be freed
+ with the shell object.
+ }
+
+ imageFactory := nil;
+
+ for j := 0 to 9 do
+ begin
+ if imageFactory = nil then
+ imageFactory := ARGB_GetWorker();
+ if VistaOrLater then
+ begin
+ d^.hBitmaps[j] := ARGB_BitmapFromIcon(imageFactory, Self^.hMemDC, p^.hIcons[j]);
+ d^.hIcons[j] := 0;
+ end
+ else
+ begin
+ d^.hBitmaps[j] := 0;
+ d^.hIcons[j] := CopyIcon(p^.hIcons[j]);
+ end;
+ end;
+
+ if imageFactory <> nil then
+ begin
+ imageFactory^.ptrVTable^.Release(imageFactory);
+ imageFactory := nil;
+ end;
+
+ inc(Self^.ProtoIconsCount);
+ pct := pct^.Next;
+ end;
+end;
+
+function ProcessRequest(hwnd: hwnd; lParam: PEnumData): BOOL; stdcall;
+var
+ pid: Integer;
+ hMirandaWorkEvent: THandle;
+ replyBits: Integer;
+ hScreenDC: THandle;
+ szBuf: array [0 .. MAX_PATH] of Char;
+begin
+ Result := True;
+ pid := 0;
+ GetWindowThreadProcessId(hwnd, @pid);
+ If pid <> 0 then
+ begin
+ // old system would get a window's pid and the module handle that created it
+ // and try to OpenEvent() a event object name to it (prefixed with a string)
+ // this was fine for most Oses (not the best way) but now actually compares
+ // the class string (a bit slower) but should get rid of those bugs finally.
+ hMirandaWorkEvent := OpenEvent(EVENT_ALL_ACCESS, False, PChar(CreateProcessUID(pid)));
+ if (hMirandaWorkEvent <> 0) then
+ begin
+ GetClassName(hwnd, szBuf, sizeof(szBuf));
+ if lstrcmp(szBuf, MirandaName) <> 0 then
+ begin
+ // opened but not valid.
+ CloseHandle(hMirandaWorkEvent);
+ Exit;
+ end; // if
+ end; // if
+ { If the event object exists, then a shlext.dll running in the instance must of created it. }
+ If hMirandaWorkEvent <> 0 then
+ begin
+ { prep the request }
+ ipcPrepareRequests(IPC_PACKET_SIZE, lParam^.ipch, REQUEST_ICONS or REQUEST_GROUPS or
+ REQUEST_CONTACTS or REQUEST_NEWICONS);
+ // slots will be in the order of icon data, groups then contacts, the first
+ // slot will contain the profile name
+ replyBits := ipcSendRequest(hMirandaWorkEvent, lParam^.hWaitFor, lParam^.ipch, 1000);
+ { replyBits will be REPLY_FAIL if the wait timed out, or it'll be the request
+ bits as sent or a series of *_NOTIMPL bits where the request bit were, if there are no
+ contacts to speak of, then don't bother showing this instance of Miranda }
+ if (replyBits <> REPLY_FAIL) and (lParam^.ipch^.ContactsBegin <> nil) then
+ begin
+ // load the address again, the server side will always overwrite it
+ lParam^.ipch^.pClientBaseAddress := lParam^.ipch;
+ // fixup all the pointers to be relative to the memory map
+ // the base pointer of the client side version of the mapped file
+ ipcFixupAddresses(False, lParam^.ipch);
+ // store the PID used to create the work event object
+ // that got replied to -- this is needed since each contact
+ // on the final menu maybe on a different instance and another OpenEvent() will be needed.
+ lParam^.pid := pid;
+ // check out the user options from the server
+ lParam^.bShouldOwnerDraw := (lParam^.ipch^.dwFlags and HIPC_NOICONS) = 0;
+ // process the icons
+ BuildSkinIcons(lParam);
+ // process other replies
+ BuildMenus(lParam);
+ end;
+ { close the work object }
+ CloseHandle(hMirandaWorkEvent);
+ end; // if
+ end; // if
+end;
+
+function TShlComRec_QueryInterface(Self: PCommon_Interface; const IID: TIID; var Obj)
+ : HResult; stdcall;
+begin
+ Pointer(Obj) := nil;
+ { IShellExtInit is given when the TShlRec is created }
+ if IsEqualIID(IID, IID_IContextMenu) or IsEqualIID(IID, IID_IContextMenu2) or
+ IsEqualIID(IID, IID_IContextMenu3) then
+ begin
+ with Self^.ptrInstance^ do
+ begin
+ Pointer(Obj) := @ContextMenu3_Interface;
+ inc(RefCount);
+ end; { with }
+ Result := S_OK;
+ end
+ else
+ begin
+ // under XP, it may ask for IShellExtInit again, this fixes the -double- click to see menus issue
+ // which was really just the object not being created
+ if IsEqualIID(IID, IID_IShellExtInit) then
+ begin
+ with Self^.ptrInstance^ do
+ begin
+ Pointer(Obj) := @ShellExtInit_Interface;
+ inc(RefCount);
+ end; // if
+ Result := S_OK;
+ end
+ else
+ begin
+ Result := CLASS_E_CLASSNOTAVAILABLE;
+ end; // if
+ end; // if
+end;
+
+function TShlComRec_AddRef(Self: PCommon_Interface): LongInt; stdcall;
+begin
+ with Self^.ptrInstance^ do
+ begin
+ inc(RefCount);
+ Result := RefCount;
+ end; { with }
+end;
+
+function TShlComRec_Release(Self: PCommon_Interface): LongInt; stdcall;
+var
+ j, c: Cardinal;
+begin
+ with Self^.ptrInstance^ do
+ begin
+ dec(RefCount);
+ Result := RefCount;
+ If RefCount = 0 then
+ begin
+ // time to go byebye.
+ with Self^.ptrInstance^ do
+ begin
+ // Note MRU menu is associated with a window (indirectly) so windows will free it.
+ // free icons!
+ if ProtoIcons <> nil then
+ begin
+ c := ProtoIconsCount;
+ while c > 0 do
+ begin
+ dec(c);
+ for j := 0 to 9 do
+ begin
+ with ProtoIcons[c] do
+ begin
+ if hIcons[j] <> 0 then
+ DestroyIcon(hIcons[j]);
+ if hBitmaps[j] <> 0 then
+ DeleteObject(hBitmaps[j]);
+ end;
+ end;
+ end;
+ FreeMem(ProtoIcons);
+ ProtoIcons := nil;
+ end; // if
+ // free IDataObject reference if pointer exists
+ if pDataObject <> nil then
+ begin
+ pDataObject^.ptrVTable^.Release(pDataObject);
+ end; // if
+ pDataObject := nil;
+ // free the heap and any memory allocated on it
+ HeapDestroy(hDllHeap);
+ // destroy the DC
+ if hMemDC <> 0 then
+ DeleteDC(hMemDC);
+ end; // with
+ // free the instance (class record) created
+ Dispose(Self^.ptrInstance);
+ dec(dllpublic.ObjectCount);
+ end; { if }
+ end; { with }
+end;
+
+function TShlComRec_Initialise(Self: PContextMenu3_Interface; pidLFolder: Pointer;
+ DObj: PDataObject_Interface; hKeyProdID: HKEY): HResult; stdcall;
+begin
+ // DObj is a pointer to an instance of IDataObject which is a pointer itself
+ // it contains a pointer to a function table containing the function pointer
+ // address of GetData() - the instance data has to be passed explicitly since
+ // all compiler magic has gone.
+ with Self^.ptrInstance^ do
+ begin
+ if DObj <> nil then
+ begin
+ Result := S_OK;
+ // if an instance already exists, free it.
+ if pDataObject <> nil then
+ pDataObject^.ptrVTable^.Release(pDataObject);
+ // store the new one and AddRef() it
+ pDataObject := DObj;
+ pDataObject^.ptrVTable^.AddRef(pDataObject);
+ end
+ else
+ begin
+ Result := E_INVALIDARG;
+ end; // if
+ end; // if
+end;
+
+function MAKE_HRESULT(Severity, Facility, Code: Integer): HResult;
+{$IFDEF FPC}
+inline;
+{$ENDIF}
+begin
+ Result := (Severity shl 31) or (Facility shl 16) or Code;
+end;
+
+function TShlComRec_QueryContextMenu(Self: PContextMenu3_Interface; Menu: hMenu;
+ indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall;
+type
+ TDllVersionInfo = record
+ cbSize: DWORD;
+ dwMajorVersion: DWORD;
+ dwMinorVersion: DWORD;
+ dwBuildNumber: DWORD;
+ dwPlatformID: DWORD;
+ end;
+
+ TDllGetVersionProc = function(var dv: TDllVersionInfo): HResult; stdcall;
+var
+ hShellInst: THandle;
+ bMF_OWNERDRAW: Boolean;
+ DllGetVersionProc: TDllGetVersionProc;
+ dvi: TDllVersionInfo;
+ ed: TEnumData;
+ hMap: THandle;
+ pipch: PHeaderIPC;
+begin
+ Result := 0;
+ if ((LOWORD(uFlags) and CMF_VERBSONLY) <> CMF_VERBSONLY) and
+ ((LOWORD(uFlags) and CMF_DEFAULTONLY) <> CMF_DEFAULTONLY) then
+ begin
+ bMF_OWNERDRAW := False;
+ // get the shell version
+ hShellInst := LoadLibrary('shell32.dll');
+ if hShellInst <> 0 then
+ begin
+ DllGetVersionProc := GetProcAddress(hShellInst, 'DllGetVersion');
+ if @DllGetVersionProc <> nil then
+ begin
+ dvi.cbSize := sizeof(TDllVersionInfo);
+ if DllGetVersionProc(dvi) >= 0 then
+ begin
+ // it's at least 4.00
+ bMF_OWNERDRAW := (dvi.dwMajorVersion > 4) or (dvi.dwMinorVersion >= 71);
+ end; // if
+ end; // if
+ FreeLibrary(hShellInst);
+ end; // if
+
+ // if we're using Vista (or later), then the ownerdraw code will be disabled, because the system draws the icons.
+ if VistaOrLater then
+ bMF_OWNERDRAW := False;
+
+ hMap := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, IPC_PACKET_SIZE,
+ IPC_PACKET_NAME);
+ If (hMap <> 0) and (GetLastError <> ERROR_ALREADY_EXISTS) then
+ begin
+ { map the memory to this address space }
+ pipch := MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ If pipch <> nil then
+ begin
+ { let the callback have instance vars }
+ ed.Self := Self^.ptrInstance;
+ // not used 'ere
+ ed.Self^.hRootMenu := Menu;
+ // store the first ID to offset with index for InvokeCommand()
+ Self^.ptrInstance^.idCmdFirst := idCmdFirst;
+ // store the starting index to offset
+ Result := idCmdFirst;
+ ed.bOwnerDrawSupported := bMF_OWNERDRAW;
+ ed.bShouldOwnerDraw := True;
+ ed.idCmdFirst := idCmdFirst;
+ ed.ipch := pipch;
+ { allocate a wait object so the ST can signal us, it can't be anon
+ since it has to used by OpenEvent() }
+ lstrcpya(@pipch^.SignalEventName, PChar(CreateUID()));
+ { create the wait wait-for-wait object }
+ ed.hWaitFor := CreateEvent(nil, False, False, pipch^.SignalEventName);
+ If ed.hWaitFor <> 0 then
+ begin
+ { enumerate all the top level windows to find all loaded MIRANDANAME
+ classes -- }
+ EnumWindows(@ProcessRequest, lParam(@ed));
+ { close the wait-for-reply object }
+ CloseHandle(ed.hWaitFor);
+ end;
+ { unmap the memory from this address space }
+ UnmapViewOfFile(pipch);
+ end; { if }
+ { close the mapping }
+ CloseHandle(hMap);
+ // use the MSDN recommended way, thou there ain't much difference
+ Result := MAKE_HRESULT(0, 0, (ed.idCmdFirst - Result) + 1);
+ end
+ else
+ begin
+ // the mapping file already exists, which is not good!
end;
-
- { this callback is triggered by the menu code and IPC is already taking place,
- just the transfer type+data needs to be setup }
- function ClearMRUIPC(
- pipch: PHeaderIPC; // IPC header info, already mapped
- hWorkThreadEvent: THandle; // event object being waited on on miranda thread
- hAckEvent: THandle; // ack event object that has been created
- psd: PMenuDrawInfo // command/draw info
- ): Integer; stdcall;
- begin
- Result := S_OK;
- ipcPrepareRequests(IPC_PACKET_SIZE, pipch, REQUEST_CLEARMRU);
- ipcSendRequest(hWorkThreadEvent, hAckEvent, pipch, 100) ;
- end; - - procedure RemoveCheckmarkSpace(hMenu: HMENU); - const - MIM_STYLE = $00000010; - MNS_CHECKORBMP = $4000000; - type - TMENUINFO = record - cbSize: DWORD; - fMask: DWORD; - dwStyle: DWORD; - cyMax: LongInt; - hbrBack: THandle; - dwContextHelpID: DWORD; - dwMenuData: Pointer; - end; - var - SetMenuInfo: function(hMenu: HMENU; var mi: TMenuInfo): Boolean; stdcall; - mi: TMENUINFO; - begin - if not VistaOrLater then Exit; - SetMenuInfo := GetProcAddress(GetModuleHandle('user32'), 'SetMenuInfo'); - if @SetMenuInfo = nil then Exit; - mi.cbSize := sizeof(mi); - mi.fMask := MIM_STYLE; - mi.dwStyle := MNS_CHECKORBMP; - SetMenuInfo(hMenu, mi); - end;
- - procedure BuildMenus(lParam: PEnumData); - {$define SHL_IDC} - {$define SHL_KEYS} - {$include shlc.inc} - {$undef SHL_KEYS} - {$undef SHL_IDC} - var - hBaseMenu: HMENU; - hGroupMenu: HMENU; - pg: PSlotIPC; - szProf: PChar; - mii: TMenuItemInfo; - j: TGroupNodeList; - p, q: PGroupNode; - Depth, Hash: Cardinal; - Token: PChar; - tk: TStrTokRec; - szBuf: PChar; - hDllHeap: THandle; - psd: PMenuDrawInfo; - c: Cardinal; - pp: ^TSlotProtoIconsArray; - begin - ZeroMemory(@mii, sizeof(mii)); - hDllHeap := lParam^.Self^.hDllHeap; - hBaseMenu := lParam^.Self^.hRootMenu; - // build an in memory tree of the groups - pg := lParam^.ipch^.GroupsBegin; - tk.szSet := ['\']; - tk.bSetTerminator := False; - j.First := nil; - j.Last := nil; - while pg <> nil do - begin - if (pg^.cbSize <> sizeof(TSlotIPC)) or (pg^.fType <> REQUEST_GROUPS) then Break; - Depth := 0; - p := j.First; // start at root again - // get the group - Integer(tk.szStr) := (Integer(pg) + sizeof(TSlotIPC)); - // find each word between \ and create sub groups if needed. - Token := StrTok(tk); - while Token <> nil do - begin - Hash := StrHash(Token); - // if the (sub)group doesn't exist, create it. - q := FindGroupNode(p, Hash, Depth); - if q = nil then - begin - q := AllocGroupNode(@j, p, Depth); - q^.Depth := Depth; - // this is the hash of this group node, but it can be anywhere - // i.e. Foo\Foo this is because each node has a different depth - // trouble is contacts don't come with depths! - q^.Hash := Hash; - // don't assume that pg^.hGroup's hash is valid for this token - // since it maybe Miranda\Blah\Blah and we have created the first node - // which maybe Miranda, thus giving the wrong hash - // since "Miranda" can be a group of it's own and a full path - q^.cchGroup := lstrlen(Token); - q^.szGroup := HeapAlloc(hDllHeap, 0, q^.cchGroup+1); - strcpy(q^.szGroup, Token); - q^.dwItems := 0; - end; - p := q; - Inc(Depth); - Token := StrTok(tk); - end; //while - pg := pg^.Next; - end; // while - // build the menus inserting into hGroupMenu which will be a submenu of - // the instance menu item. e.g. Miranda -> [Groups ->] contacts - hGroupMenu := CreatePopupMenu();
-
- // allocate MRU menu, this will be associated with the higher up menu
- // so doesn't need to be freed (unless theres no MRUs items attached)
- // This menu is per process but the handle is stored globally (like a stack)
- lParam^.Self^.hRecentMenu := CreatePopupMenu();
- lParam^.Self^.RecentCount := 0; - // create group menus only if they exist! - if lParam^.ipch^.GroupsBegin <> nil then - begin - BuildMenuGroupTree(j.First, lParam, hGroupMenu); - // add contacts that have a group somewhere - BuildContactTree(j.First, lParam); - end; - // - mii.cbSize := sizeof(TMenuItemInfo); - mii.fMask := MIIM_ID or MIIM_TYPE or MIIM_DATA; - // add all the contacts that have no group (which maybe all of them) - pg := lParam^.ipch^.ContactsBegin; - while pg <> nil do - begin - if (pg^.cbSize <> sizeof(TSlotIPC)) or (pg^.fType <> REQUEST_CONTACTS) then Break; - if pg^.hGroup = 0 then - begin - DecideMenuItemInfo(pg, nil, mii, lParam);
- BuildMRU(pg, mii, lParam); - InsertMenuItem(hGroupMenu, $FFFFFFFF, True, mii); - end; //if - pg := pg^.Next; - end; //while
-
- // insert MRU menu as a submenu of the contact menu only if
- // the MRU list has been created, the menu popup will be deleted by itself
- if lParam^.Self^.RecentCount > 0 then
- begin
-
- // insert seperator and 'clear list' menu
- mii.fType := MFT_SEPARATOR;
- mii.fMask := MIIM_TYPE;
- InsertMenuitem(lParam^.Self^.hRecentMenu, $FFFFFFFF, True, mii);
-
- // insert 'clear MRU' item and setup callback
- mii.fMask := MIIM_TYPE or MIIM_ID or MIIM_DATA;
- mii.wID := lParam^.idCmdFirst;
- Inc(lParam^.idCmdFirst);
- mii.fType := MFT_STRING;
- mii.dwTypeData := lParam^.ipch^.ClearEntries; // "Clear entries"
- //allocate menu substructure
- psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
- psd^.fTypes := [dtCommand];
- psd^.MenuCommandCallback := @ClearMRUIPC;
- psd^.wID := mii.wID;
- // this is needed because there is a clear list command per each process.
- psd^.pid := lParam^.pid;
- mii.dwItemData := Integer(psd);
- InsertMenuitem(lParam^.Self^.hRecentMenu, $FFFFFFFF, True, mii);
-
- // insert MRU submenu into group menu (with) ownerdraw support as needed
- psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo));
- psd^.szProfile := 'MRU';
- psd^.fTypes := [dtGroup];
- // the IPC string pointer wont be around forever, must make a copy
- psd^.cch := strlen(lParam^.ipch^.MRUMenuName);
- psd^.szText := HeapAlloc(hDllHeap, 0, psd^.cch+1);
- lstrcpyn(psd^.szText, lParam^.ipch^.MRUMenuName, sizeof(lParam^.ipch^.MRUMenuName)-1);
-
- mii.dwItemData := Integer(psd);
- if (lParam^.bOwnerDrawSupported) and (lParam^.bShouldOwnerDraw) then
- begin
- mii.fType := MFT_OWNERDRAW;
- Pointer(mii.dwTypeData) := psd;
- end else
- begin
- mii.dwTypeData := lParam^.ipch^.MRUMenuName; // 'Recent';
- end;
- mii.wID := lParam^.idCmdFirst;
- Inc(lParam^.idCmdFirst);
- mii.fMask := MIIM_TYPE or MIIM_SUBMENU or MIIM_DATA or MIIM_ID;
- mii.hSubMenu := lParam^.Self^.hRecentMenu;
- InsertMenuItem(hGroupMenu, 0, True, mii);
- end
- else
- begin
- // no items were attached to the MRU, delete the MRU menu
- DestroyMenu(lParam^.Self^.hRecentMenu);
- lParam^.Self^.hRecentMenu := 0;
- end; -
- // allocate display info/memory for "Miranda" string
- - mii.cbSize := sizeof(TMenuItemInfo); - mii.fMask := MIIM_ID or MIIM_DATA or MIIM_TYPE or MIIM_SUBMENU; - if VistaOrLater then - begin - mii.fMask := MIIM_ID or MIIM_DATA or MIIM_FTYPE or MIIM_SUBMENU or MIIM_STRING or MIIM_BITMAP; - end; - mii.hSubMenu := hGroupMenu; - - // by default, the menu will have space for icons and checkmarks (on Vista+) and we don't need this - RemoveCheckmarkSpace(hGroupMenu); -
- - psd := HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo)); - psd^.cch := strlen(lParam^.ipch^.MirandaName); - psd^.szText := HeapAlloc(hDllHeap, 0, psd^.cch+1); - lstrcpyn(psd^.szText, lParam^.ipch^.MirandaName, sizeof(lParam^.ipch^.MirandaName)-1); - // there may not be a profile name - pg := lParam^.ipch^.DataPtr; - psd^.szProfile := nil; - if ((pg <> nil) and (pg^.Status = STATUS_PROFILENAME)) then - begin - psd^.szProfile := HeapAlloc(hDllHeap, 0, pg^.cbStrSection); - strcpy(psd^.szProfile, PChar(Integer(pg) + sizeof(TSlotIPC))); - end; //if - // owner draw menus need ID's - mii.wID := lParam^.idCmdFirst; - Inc(lParam^.idCmdFirst); - psd^.fTypes := [dtEntry]; - psd^.wID := mii.wID; - psd^.hContact := 0; - // get Miranda's icon or bitmap - c := lParam^.Self^.protoIconsCount; - pp := lParam^.Self^.protoIcons; - while c > 0 do - begin - dec(c); - if (pp[c].pid = lParam^.pid) and (pp[c].hProto = 0) then - begin - // either of these can be 0 - psd^.hStatusIcon := pp[c].hIcons[0]; - mii.hBmpItem := pp[c].hBitmaps[0]; - break; - end; //if - end; //while - mii.dwItemData := Integer(psd); - if ((lParam^.bOwnerDrawSupported) and (lParam^.bShouldOwnerDraw)) then - begin - mii.fType := MFT_OWNERDRAW; - Pointer(mii.dwTypeData) := psd; - end else begin - mii.fType := MFT_STRING; - mii.dwTypeData := lParam^.ipch^.MirandaName; - mii.cch := sizeof(lParam^.ipch^.MirandaName)-1; - end; - // add it all - InsertMenuItem(hBaseMenu, 0, True, mii); - // free the group tree - FreeGroupTreeAndEmptyGroups(hGroupMenu, nil, j.First); - end; - - procedure BuildSkinIcons(lParam: PEnumData); - var - pct: PSlotIPC; - p, d: PSlotProtoIcons; - Self: PShlComRec; - j: Cardinal; - imageFactory: PImageFactory_Interface; - begin - pct := lParam^.ipch^.NewIconsBegin; - self := lParam^.Self; - while (pct <> nil) do - begin - if (pct^.cbSize <> sizeof(TSlotIPC)) or (pct^.fType <> REQUEST_NEWICONS) then Break; - Integer(p) := Integer(pct) + sizeof(TSlotIPC); - ReAllocMem(self^.protoIcons, (self^.protoIconsCount+1)*sizeof(TSlotProtoIcons)); - d := @self^.protoIcons[self^.protoIconsCount]; - CopyMemory(d,p,sizeof(TSlotProtoIcons)); - - { - If using Vista (or later), clone all the icons into bitmaps and keep these around, - if using anything older, just use the default code, the bitmaps (and or icons) will be freed - with the shell object. - } - - imageFactory := nil; - - for j := 0 to 9 do - begin - if imageFactory = nil then imageFactory := ARGB_GetWorker(); - if VistaOrLater then - begin - d^.hBitmaps[j] := ARGB_BitmapFromIcon(imageFactory, self^.hMemDC, p^.hIcons[j] ); - d^.hIcons[j] := 0; - end else - begin - d^.hBitmaps[j] := 0; - d^.hIcons[j] := CopyIcon(p^.hIcons[j]); - end; - end; - - if imageFactory <> nil then - begin - imageFactory^.ptrVTable^.Release(imageFactory); - imageFactory := nil; - end; - - inc(self^.protoIconsCount); - pct := pct^.Next; - end; - end; - - function ProcessRequest(hwnd: HWND; lParam: PEnumData): BOOL; stdcall; - var - pid: Integer; - hMirandaWorkEvent: THandle; - replyBits: Integer; - hScreenDC: THandle; - szBuf: array[0..MAX_PATH] of Char; - begin - Result := True; - pid := 0; - GetWindowThreadProcessId(hwnd, @pid); - If pid <> 0 then - begin - // old system would get a window's pid and the module handle that created it - // and try to OpenEvent() a event object name to it (prefixed with a string) - // this was fine for most Oses (not the best way) but now actually compares - // the class string (a bit slower) but should get rid of those bugs finally. - hMirandaWorkEvent := OpenEvent(EVENT_ALL_ACCESS, False, PChar(CreateProcessUID(pid))); - if (hMirandaWorkEvent <> 0) then - begin - GetClassName(hwnd, szBuf, sizeof(szBuf)); - if lstrcmp(szBuf, MIRANDANAME) <> 0 then - begin - // opened but not valid. - CloseHandle(hMirandaWorkEvent); Exit; - end; //if - end; //if
- { If the event object exists, then a shlext.dll running in the instance must of created it. } - If hMirandaWorkEvent <> 0 then - begin - { prep the request } - ipcPrepareRequests(IPC_PACKET_SIZE, lParam^.ipch, REQUEST_ICONS or REQUEST_GROUPS or REQUEST_CONTACTS or REQUEST_NEWICONS); - // slots will be in the order of icon data, groups then contacts, the first - // slot will contain the profile name - replyBits := ipcSendRequest(hMirandaWorkEvent, lParam^.hWaitFor, lParam^.ipch, 1000); - { replyBits will be REPLY_FAIL if the wait timed out, or it'll be the request - bits as sent or a series of *_NOTIMPL bits where the request bit were, if there are no - contacts to speak of, then don't bother showing this instance of Miranda } - if (replyBits <> REPLY_FAIL) and (lParam^.ipch^.ContactsBegin <> nil) then - begin - // load the address again, the server side will always overwrite it - lParam^.ipch^.pClientBaseAddress := lParam^.ipch; - // fixup all the pointers to be relative to the memory map - // the base pointer of the client side version of the mapped file - ipcFixupAddresses(False, lParam^.ipch); - // store the PID used to create the work event object - // that got replied to -- this is needed since each contact - // on the final menu maybe on a different instance and another OpenEvent() will be needed. - lParam^.pid := pid; - // check out the user options from the server - lParam^.bShouldOwnerDraw := (lParam^.ipch^.dwFlags and HIPC_NOICONS) = 0; - // process the icons - BuildSkinIcons(lParam); - // process other replies - BuildMenus(lParam); - end; - { close the work object } - CloseHandle(hMirandaWorkEvent); - end; //if - end; //if - end; - - function TShlComRec_QueryInterface(Self: PCommon_Interface; - const IID: TIID; var Obj): HResult; stdcall; - begin - Pointer(Obj) := nil; - { IShellExtInit is given when the TShlRec is created } - if IsEqualIID(IID, IID_IContextMenu) - or IsEqualIID(IID, IID_IContextMenu2) - or IsEqualIID(IID, IID_IContextMenu3) then - begin - with Self^.ptrInstance^ do - begin - Pointer(Obj) := @ContextMenu3_Interface; - Inc(RefCount); - end; {with} - Result := S_OK; - end else begin - // under XP, it may ask for IShellExtInit again, this fixes the -double- click to see menus issue - // which was really just the object not being created - if IsEqualIID(IID, IID_IShellExtInit) then - begin - with Self^.ptrInstance^ do - begin - Pointer(Obj) := @ShellExtInit_Interface; - Inc(RefCount); - end; //if - Result := S_OK; - end else begin - Result := CLASS_E_CLASSNOTAVAILABLE; - end; //if - end; //if - end; - - function TShlComRec_AddRef(Self: PCommon_Interface): LongInt; stdcall; - begin - with Self^.ptrInstance^ do - begin - Inc(RefCount); - Result := RefCount; - end; {with} - end; - - function TShlComRec_Release(Self: PCommon_Interface): LongInt; stdcall; - var - j, c: Cardinal; - begin - with Self^.ptrInstance^ do - begin - Dec(RefCount); - Result := RefCount; - If RefCount = 0 then - begin - // time to go byebye. - with Self^.ptrInstance^ do + end
+ else
+ begin
+ // same as giving a SEVERITY_SUCCESS, FACILITY_NULL, since that
+ // just clears the higher bits, which is done anyway
+ Result := MAKE_HRESULT(0, 0, 1);
+ end; // if
+end;
+
+function TShlComRec_GetCommandString(Self: PContextMenu3_Interface; idCmd, uType: UINT;
+ pwReserved: PUINT; pszName: PChar; cchMax: UINT): HResult; stdcall;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function ipcGetFiles(pipch: PHeaderIPC; pDataObject: PDataObject_Interface;
+ const hContact: THandle): Integer;
+type
+ TDragQueryFile = function(hDrop: THandle; fileIndex: Integer; FileName: PChar;
+ cbSize: Integer): Integer; stdcall;
+var
+ fet: TFormatEtc;
+ stgm: TStgMedium;
+ pct: PSlotIPC;
+ iFile: Cardinal;
+ iFileMax: Cardinal;
+ hShell: THandle;
+ DragQueryFile: TDragQueryFile;
+ cbSize: Integer;
+ hDrop: THandle;
+begin
+ Result := E_INVALIDARG;
+ hShell := LoadLibrary('shell32.dll');
+ if hShell <> 0 then
+ begin
+ DragQueryFile := GetProcAddress(hShell, 'DragQueryFileA');
+ if @DragQueryFile <> nil then
+ begin
+ fet.cfFormat := CF_HDROP;
+ fet.ptd := nil;
+ fet.dwAspect := DVASPECT_CONTENT;
+ fet.lindex := -1;
+ fet.tymed := TYMED_HGLOBAL;
+ Result := pDataObject^.ptrVTable^.GetData(pDataObject, fet, stgm);
+ if Result = S_OK then
+ begin
+ // FIX, actually lock the global object and get a pointer
+ Pointer(hDrop) := GlobalLock(stgm.hGlobal);
+ if hDrop <> 0 then
+ begin
+ // get the maximum number of files
+ iFileMax := DragQueryFile(stgm.hGlobal, $FFFFFFFF, nil, 0);
+ iFile := 0;
+ while iFile < iFileMax do
+ begin
+ // get the size of the file path
+ cbSize := DragQueryFile(stgm.hGlobal, iFile, nil, 0);
+ // get the buffer
+ pct := ipcAlloc(pipch, cbSize + 1); // including null term
+ // allocated?
+ if pct = nil then
+ break;
+ // store the hContact
+ pct^.hContact := hContact;
+ // copy it to the buffer
+ DragQueryFile(stgm.hGlobal, iFile, PChar(Integer(pct) + sizeof(TSlotIPC)),
+ pct^.cbStrSection);
+ // next file
+ inc(iFile);
+ end; // while
+ // store the number of files
+ pipch^.Slots := iFile;
+ GlobalUnlock(stgm.hGlobal);
+ end; // if hDrop check
+ // release the mediumn the lock may of failed
+ ReleaseStgMedium(stgm);
+ end; // if
+ end; // if
+ // free the dll
+ FreeLibrary(hShell);
+ end; // if
+end;
+
+function RequestTransfer(Self: PShlComRec; idxCmd: Integer): Integer;
+var
+ hMap: THandle;
+ pipch: PHeaderIPC;
+ mii: TMenuItemInfo;
+ hTransfer: THandle;
+ psd: PMenuDrawInfo;
+ hReply: THandle;
+ replyBits: Integer;
+begin
+ Result := E_INVALIDARG;
+ // get the contact information
+ mii.cbSize := sizeof(TMenuItemInfo);
+ mii.fMask := MIIM_ID or MIIM_DATA;
+ if GetMenuItemInfo(Self^.hRootMenu, Self^.idCmdFirst + idxCmd, False, mii) then
+ begin
+ // get the pointer
+ int_ptr(psd) := mii.dwItemData;
+ // the ID stored in the item pointer and the ID for the menu must match
+ if (psd = nil) or (psd^.wID <> mii.wID) then
+ begin
+ // MessageBox(0,'ptr assocated with menu is NULL','',MB_OK);
+ Exit;
+ end; // if
+ end
+ else
+ begin
+ // MessageBox(0,'GetMenuItemInfo failed?','',MB_OK);
+ // couldn't get the info, can't start the transfer
+ Result := E_INVALIDARG;
+ Exit;
+ end; // if
+ // is there an IDataObject instance?
+ if Self^.pDataObject <> nil then
+ begin
+ // OpenEvent() the work object to see if the instance is still around
+ hTransfer := OpenEvent(EVENT_ALL_ACCESS, False, PChar(CreateProcessUID(psd^.pid)));
+ if hTransfer <> 0 then
+ begin
+ // map the ipc file again
+ hMap := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, IPC_PACKET_SIZE,
+ IPC_PACKET_NAME);
+ if (hMap <> 0) and (GetLastError <> ERROR_ALREADY_EXISTS) then
+ begin
+ // map it to process
+ pipch := MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ if pipch <> nil then
+ begin
+ // create the name of the object to be signalled by the ST
+ lstrcpya(pipch^.SignalEventName, PChar(CreateUID()));
+ // create it
+ hReply := CreateEvent(nil, False, False, pipch^.SignalEventName);
+ if hReply <> 0 then
+ begin
+ if dtCommand in psd^.fTypes then
+ begin
+ if Assigned(psd^.MenuCommandCallback) then
+ Result := psd^.MenuCommandCallback(pipch, hTransfer, hReply, psd);
+ end
+ else
+ begin
+
+ // prepare the buffer
+ ipcPrepareRequests(IPC_PACKET_SIZE, pipch, REQUEST_XFRFILES);
+ // get all the files into the packet
+ if ipcGetFiles(pipch, Self^.pDataObject, psd^.hContact) = S_OK then
+ begin
+ // need to wait for the ST to open the mapping object
+ // since if we close it before it's opened it the data it
+ // has will be undefined
+ replyBits := ipcSendRequest(hTransfer, hReply, pipch, 200);
+ if replyBits <> REPLY_FAIL then
begin
- // Note MRU menu is associated with a window (indirectly) so windows will free it. - // free icons! - if protoIcons <> nil then - begin - c := protoIconsCount; - while c > 0 do - begin - dec(c); - for j := 0 to 9 do - begin - with protoIcons[c] do - begin - if hIcons[j] <> 0 then DestroyIcon(hIcons[j]); - if hBitmaps[j] <> 0 then DeleteObject(hBitmaps[j]); - end; - end; - end; - FreeMem(protoIcons); protoIcons := nil; - end; //if - // free IDataObject reference if pointer exists - if pDataObject <> nil then - begin - pDataObject^.ptrvTable^.Release(pDataObject); - end; //if - pDataObject := nil; - // free the heap and any memory allocated on it - HeapDestroy(hDllHeap); - // destroy the DC - if hMemDC <> 0 then DeleteDC(hMemDC); - end; //with - // free the instance (class record) created - Dispose(Self^.ptrInstance); - Dec(dllpublic.ObjectCount); - end; {if} - end; {with} - end; - - function TShlComRec_Initialise(Self: PContextMenu3_Interface; - pidLFolder: Pointer; DObj: PDataObject_Interface; hKeyProdID: HKEY): HResult; stdcall; - begin - // DObj is a pointer to an instance of IDataObject which is a pointer itself - // it contains a pointer to a function table containing the function pointer - // address of GetData() - the instance data has to be passed explicitly since - // all compiler magic has gone. - with Self^.ptrInstance^ do - begin - if DObj <> nil then - begin - Result := S_OK; - // if an instance already exists, free it. - if pDataObject <> nil then pDataObject^.ptrVTable^.Release(pDataObject); - // store the new one and AddRef() it - pDataObject := DObj; - pDataObject^.ptrVTable^.AddRef(pDataObject); - end else begin - Result := E_INVALIDARG; - end; //if - end; //if - end; - - function MAKE_HRESULT(Severity, Facility, Code: Integer): HResult; - {$ifdef FPC} - inline; - {$endif} - begin - Result := (Severity shl 31) or (Facility shl 16) or Code; - end; - - function TShlComRec_QueryContextMenu(Self: PContextMenu3_Interface; - Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall; - type - TDllVersionInfo = record - cbSize: DWORD; - dwMajorVersion: DWORD; - dwMinorVersion: DWORD; - dwBuildNumber: DWORD; - dwPlatformID: DWORD; - end; - TDllGetVersionProc = function(var dv: TDllVersionInfo): HResult; stdcall; - var - hShellInst: THandle; - bMF_OWNERDRAW: Boolean; - DllGetVersionProc: TDllGetVersionProc; - dvi: TDllVersionInfo; - ed: TEnumData; - hMap: THandle; - pipch: PHeaderIPC; - begin - Result := 0; - if ((LOWORD(uFlags) and CMF_VERBSONLY) <> CMF_VERBSONLY) and ((LOWORD(uFlags) and CMF_DEFAULTONLY) <> CMF_DEFAULTONLY) then - begin - bMF_OWNERDRAW := False; - // get the shell version - hShellInst := LoadLibrary('shell32.dll'); - if hShellInst <> 0 then - begin - DllGetVersionProc := GetProcAddress(hShellInst, 'DllGetVersion'); - if @DllGetVersionProc <> nil then - begin - dvi.cbSize := sizeof(TDllVersionInfo); - if DllGetVersionProc(dvi) >= 0 then - begin - // it's at least 4.00 - bMF_OWNERDRAW := (dvi.dwMajorVersion > 4) or (dvi.dwMinorVersion >= 71); - end; //if - end; //if - FreeLibrary(hShellInst); - end; //if - - // if we're using Vista (or later), then the ownerdraw code will be disabled, because the system draws the icons. - if VistaOrLater then bMF_OWNERDRAW := False; - - hMap := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, IPC_PACKET_SIZE, IPC_PACKET_NAME); - If (hMap <> 0) and (GetLastError <> ERROR_ALREADY_EXISTS) then - begin - { map the memory to this address space } - pipch := MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); - If pipch <> nil then - begin - { let the callback have instance vars } - ed.Self := Self^.ptrInstance; - // not used 'ere - ed.Self^.hRootMenu := Menu; - // store the first ID to offset with index for InvokeCommand() - Self^.ptrInstance^.idCmdFirst := idCmdFirst; - // store the starting index to offset - Result := idCmdFirst; - ed.bOwnerDrawSupported := bMF_OWNERDRAW; - ed.bShouldOwnerDraw := True; - ed.idCmdFirst := idCmdFirst; - ed.ipch := pipch; - { allocate a wait object so the ST can signal us, it can't be anon - since it has to used by OpenEvent() } - strcpy(@pipch^.SignalEventName, PChar(CreateUID())); - { create the wait wait-for-wait object } - ed.hWaitFor := CreateEvent(nil, False, False, pipch^.SignalEventName); - If ed.hWaitFor <> 0 then - begin - { enumerate all the top level windows to find all loaded MIRANDANAME - classes -- } - EnumWindows(@ProcessRequest, lParam(@ed)); - { close the wait-for-reply object } - CloseHandle(ed.hWaitFor); - end; - { unmap the memory from this address space } - UnmapViewOfFile(pipch); - end; {if} - { close the mapping } - CloseHandle(hMap); - // use the MSDN recommended way, thou there ain't much difference - Result := MAKE_HRESULT(0, 0, (ed.idCmdFirst - Result) + 1); - end else begin - // the mapping file already exists, which is not good! - end; - end else begin - // same as giving a SEVERITY_SUCCESS, FACILITY_NULL, since that - // just clears the higher bits, which is done anyway - Result := MAKE_HRESULT(0, 0, 1); - end; //if - end; - - function TShlComRec_GetCommandString(Self: PContextMenu3_Interface; - idCmd, uType: UINT; pwReserved: PUINT; pszName: PChar; cchMax: UINT): HResult; stdcall; - begin - Result := E_NOTIMPL; - end; - - function ipcGetFiles(pipch: PHeaderIPC; pDataObject: PDataObject_Interface; const hContact: THandle): Integer; - type - TDragQueryFile = function(hDrop: THandle; fileIndex: Integer; FileName: PChar; cbSize: Integer): Integer; stdcall; - var - fet: TFormatEtc; - stgm: TStgMedium; - pct: PSlotIPC; - iFile: Cardinal; - iFileMax: Cardinal; - hShell: THandle; - DragQueryFile: TDragQueryFile; - cbSize: Integer; - hDrop: THandle; - begin - Result := E_INVALIDARG; - hShell := LoadLibrary('shell32.dll'); - if hShell <> 0 then - begin - DragQueryFile := GetProcAddress(hShell, 'DragQueryFileA'); - if @DragQueryFile <> nil then - begin - fet.cfFormat := CF_HDROP; - fet.ptd := nil; - fet.dwAspect := DVASPECT_CONTENT; - fet.lindex := -1; - fet.tymed := TYMED_HGLOBAL; - Result := pDataObject^.ptrVTable^.GetData(pDataObject, fet, stgm); - if Result = S_OK then - begin - // FIX, actually lock the global object and get a pointer - Pointer(hDrop) := GlobalLock(stgm.hGlobal); - if hDrop <> 0 then - begin - // get the maximum number of files - iFileMax := DragQueryFile(stgm.hGlobal, $FFFFFFFF, nil, 0); - iFile := 0; - while iFile < iFileMax do - begin - // get the size of the file path - cbSize := DragQueryFile(stgm.hGlobal, iFile, nil, 0); - // get the buffer - pct := ipcAlloc(pipch, cbSize + 1); // including null term - // allocated? - if pct = nil then Break; - // store the hContact - pct^.hContact := hContact; - // copy it to the buffer - DragQueryFile(stgm.hGlobal, iFile, PChar(Integer(pct)+sizeof(TSlotIPC)), pct^.cbStrSection); - // next file - Inc(iFile); - end; //while - // store the number of files - pipch^.Slots := iFile; - GlobalUnlock(stgm.hGlobal); - end; //if hDrop check - // release the mediumn the lock may of failed - ReleaseStgMedium(stgm); - end; //if - end; //if - // free the dll - FreeLibrary(hShell); - end; //if - end; - - function RequestTransfer(Self: PShlComRec; idxCmd: Integer): Integer; - var - hMap: THandle; - pipch: PHeaderIPC; - mii: TMenuItemInfo; - hTransfer: THandle; - psd: PMenuDrawInfo; - hReply: THandle; - replyBits: Integer; + // they got the files!
+ Result := S_OK;
+ end; // if
+ end;
+
+ end;
+ // close the work object name
+ CloseHandle(hReply);
+ end; // if
+ // unmap it from this process
+ UnmapViewOfFile(pipch);
+ end; // if
+ // close the map
+ CloseHandle(hMap);
+ end; // if
+ // close the handle to the ST object name
+ CloseHandle(hTransfer);
+ end; // if
+ end // if;
+end;
+
+function TShlComRec_InvokeCommand(Self: PContextMenu3_Interface;
+ var lpici: TCMInvokeCommandInfo): HResult; stdcall;
+begin
+ Result := RequestTransfer(Self^.ptrInstance, LOWORD(Integer(lpici.lpVerb)));
+end;
+
+function TShlComRec_HandleMenuMsgs(Self: PContextMenu3_Interface; uMsg: UINT; wParam: wParam;
+ lParam: lParam; pResult: PLResult): HResult;
+const
+ WM_DRAWITEM = $002B;
+ WM_MEASUREITEM = $002C;
+var
+ dwi: PDrawItemStruct;
+ msi: PMeasureItemStruct;
+ psd: PMenuDrawInfo;
+ ncm: TNonClientMetrics;
+ hOldFont: THandle;
+ hFont: THandle;
+ tS: TSize;
+ dx: Integer;
+ hBr: HBRUSH;
+ icorc: TRect;
+ hMemDC: HDC;
+begin
+ pResult^ := Integer(True);
+ if (uMsg = WM_DRAWITEM) and (wParam = 0) then
+ begin
+ // either a main sub menu, a group menu or a contact
+ dwi := PDrawItemStruct(lParam);
+ int_ptr(psd) := dwi^.itemData;
+ // don't fill
+ SetBkMode(dwi^.HDC, TRANSPARENT);
+ // where to draw the icon?
+ icorc.Left := 0;
+ // center it
+ with dwi^ do
+ icorc.Top := rcItem.Top + ((rcItem.Bottom - rcItem.Top) div 2) - (16 div 2);
+ icorc.Right := icorc.Left + 16;
+ icorc.Bottom := icorc.Top + 16;
+ // draw for groups
+ if (dtGroup in psd^.fTypes) or (dtEntry in psd^.fTypes) then
+ begin
+ hBr := GetSysColorBrush(COLOR_MENU);
+ FillRect(dwi^.HDC, dwi^.rcItem, hBr);
+ DeleteObject(hBr);
+ //
+ if (ODS_SELECTED and dwi^.itemState = ODS_SELECTED) then
+ begin
+ // only do this for entry menu types otherwise a black mask
+ // is drawn under groups
+ hBr := GetSysColorBrush(COLOR_HIGHLIGHT);
+ FillRect(dwi^.HDC, dwi^.rcItem, hBr);
+ DeleteObject(hBr);
+ SetTextColor(dwi^.HDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ end; // if
+ // draw icon
+ with dwi^, icorc do
+ begin
+ if (ODS_SELECTED and dwi^.itemState) = ODS_SELECTED then
+ begin
+ hBr := GetSysColorBrush(COLOR_HIGHLIGHT);
+ end
+ else
+ begin
+ hBr := GetSysColorBrush(COLOR_MENU);
+ end; // if
+ DrawIconEx(HDC, Left + 1, Top, psd^.hStatusIcon, 16, 16, // width, height
+ 0, // step
+ hBr, // brush
+ DI_NORMAL);
+ DeleteObject(hBr);
+ end; // with
+ // draw the text
+ with dwi^ do
+ begin
+ inc(rcItem.Left, ((rcItem.Bottom - rcItem.Top) - 2));
+ DrawText(HDC, psd^.szText, psd^.cch, rcItem, DT_NOCLIP or DT_NOPREFIX or
+ DT_SINGLELINE or DT_VCENTER);
+ // draw the name of the database text if it's there
+ if psd^.szProfile <> nil then
+ begin
+ GetTextExtentPoint32(dwi^.HDC, psd^.szText, psd^.cch, tS);
+ inc(rcItem.Left, tS.cx + 8);
+ SetTextColor(HDC, GetSysColor(COLOR_GRAYTEXT));
+ DrawText(HDC, psd^.szProfile, lstrlena(psd^.szProfile), rcItem,
+ DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER);
+ end; // if
+ end; // with
+ end
+ else
+ begin
+ // it's a contact!
+ hBr := GetSysColorBrush(COLOR_MENU);
+ FillRect(dwi^.HDC, dwi^.rcItem, hBr);
+ DeleteObject(hBr);
+ if ODS_SELECTED and dwi^.itemState = ODS_SELECTED then
+ begin
+ hBr := GetSysColorBrush(COLOR_HIGHLIGHT);
+ FillRect(dwi^.HDC, dwi^.rcItem, hBr);
+ DeleteObject(hBr);
+ SetTextColor(dwi^.HDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ end;
+ // draw icon
+ with dwi^, icorc do
+ begin
+ if (ODS_SELECTED and dwi^.itemState) = ODS_SELECTED then
+ begin
+ hBr := GetSysColorBrush(COLOR_HIGHLIGHT);
+ end
+ else
+ begin
+ hBr := GetSysColorBrush(COLOR_MENU);
+ end; // if
+ DrawIconEx(HDC, Left + 2, Top, psd^.hStatusIcon, 16, 16, // width, height
+ 0, // step
+ hBr, // brush
+ DI_NORMAL);
+ DeleteObject(hBr);
+ end; // with
+ // draw the text
+ with dwi^ do
+ begin
+ inc(rcItem.Left, (rcItem.Bottom - rcItem.Top) + 1);
+ DrawText(HDC, psd^.szText, psd^.cch, rcItem, DT_NOCLIP or DT_NOPREFIX or
+ DT_SINGLELINE or DT_VCENTER);
+ end; // with
+ end; // if
+ end
+ else if (uMsg = WM_MEASUREITEM) then
+ begin
+ // don't check if it's really a menu
+ msi := PMeasureItemStruct(lParam);
+ int_ptr(psd) := msi^.itemData;
+ ncm.cbSize := sizeof(TNonClientMetrics);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @ncm, 0);
+ // create the font used in menus, this font should be cached somewhere really
+{$IFDEF FPC}
+ hFont := CreateFontIndirect(@ncm.lfMenuFont);
+{$ELSE}
+ hFont := CreateFontIndirect(ncm.lfMenuFont);
+{$ENDIF}
+ hMemDC := Self^.ptrInstance^.hMemDC;
+ // select in the font
+ hOldFont := SelectObject(hMemDC, hFont);
+ // default to an icon
+ dx := 16;
+ // get the size 'n' account for the icon
+ GetTextExtentPoint32(hMemDC, psd^.szText, psd^.cch, tS);
+ inc(dx, tS.cx);
+ // main menu item?
+ if psd^.szProfile <> nil then
+ begin
+ GetTextExtentPoint32(hMemDC, psd^.szProfile, lstrlena(psd^.szProfile), tS);
+ inc(dx, tS.cx);
+ end;
+ // store it
+ msi^.itemWidth := dx + Integer(ncm.iMenuWidth);
+ msi^.itemHeight := Integer(ncm.iMenuHeight) + 2;
+ if tS.cy > msi^.itemHeight then
+ inc(msi^.itemHeight, tS.cy - msi^.itemHeight);
+ // clean up
+ SelectObject(hMemDC, hOldFont);
+ DeleteObject(hFont);
+ end;
+ Result := S_OK;
+end;
+
+function TShlComRec_HandleMenuMsg(Self: PContextMenu3_Interface; uMsg: UINT; wParam: wParam;
+ lParam: lParam): HResult; stdcall;
+var
+ Dummy: HResult;
+begin
+ Result := TShlComRec_HandleMenuMsgs(Self, uMsg, wParam, lParam, @Dummy);
+end;
+
+function TShlComRec_HandleMenuMsg2(Self: PContextMenu3_Interface; uMsg: UINT; wParam: wParam;
+ lParam: lParam; PLResult: Pointer { ^LResult } ): HResult; stdcall;
+var
+ Dummy: HResult;
+begin
+ // this will be null if a return value isn't needed.
+ if PLResult = nil then
+ PLResult := @Dummy;
+ Result := TShlComRec_HandleMenuMsgs(Self, uMsg, wParam, lParam, PLResult);
+end;
+
+function TShlComRec_Create: PShlComRec;
+var
+ DC: HDC;
+begin
+ New(Result);
+ { build all the function tables for interfaces }
+ with Result^.ShellExtInit_Interface do
+ begin
+ { this is only owned by us... }
+ ptrVTable := @vTable;
+ { IUnknown }
+ vTable.QueryInterface := @TShlComRec_QueryInterface;
+ vTable.AddRef := @TShlComRec_AddRef;
+ vTable.Release := @TShlComRec_Release;
+ { IShellExtInit }
+ vTable.Initialise := @TShlComRec_Initialise;
+ { instance of a TShlComRec }
+ ptrInstance := Result;
+ end;
+ with Result^.ContextMenu3_Interface do
+ begin
+ ptrVTable := @vTable;
+ { IUnknown }
+ vTable.QueryInterface := @TShlComRec_QueryInterface;
+ vTable.AddRef := @TShlComRec_AddRef;
+ vTable.Release := @TShlComRec_Release;
+ { IContextMenu }
+ vTable.QueryContextMenu := @TShlComRec_QueryContextMenu;
+ vTable.InvokeCommand := @TShlComRec_InvokeCommand;
+ vTable.GetCommandString := @TShlComRec_GetCommandString;
+ { IContextMenu2 }
+ vTable.HandleMenuMsg := @TShlComRec_HandleMenuMsg;
+ { IContextMenu3 }
+ vTable.HandleMenuMsg2 := @TShlComRec_HandleMenuMsg2;
+ { instance data }
+ ptrInstance := Result;
+ end;
+ { initalise variables }
+ Result^.RefCount := 1;
+ Result^.hDllHeap := HeapCreate(0, 0, 0);
+ Result^.hRootMenu := 0;
+ Result^.hRecentMenu := 0;
+ Result^.RecentCount := 0;
+ Result^.idCmdFirst := 0;
+ Result^.pDataObject := nil;
+ Result^.ProtoIcons := nil;
+ Result^.ProtoIconsCount := 0;
+ // create an inmemory DC
+ DC := GetDC(0);
+ Result^.hMemDC := CreateCompatibleDC(DC);
+ ReleaseDC(0, DC);
+ { keep count on the number of objects }
+ inc(dllpublic.ObjectCount);
+end;
+
+{ IClassFactory }
+
+type
+
+ PVTable_IClassFactory = ^TVTable_IClassFactory;
+
+ TVTable_IClassFactory = record
+ { IUnknown }
+ QueryInterface: Pointer;
+ AddRef: Pointer;
+ Release: Pointer;
+ { IClassFactory }
+ CreateInstance: Pointer;
+ LockServer: Pointer;
+ end;
+
+ PClassFactoryRec = ^TClassFactoryRec;
+
+ TClassFactoryRec = record
+ ptrVTable: PVTable_IClassFactory;
+ vTable: TVTable_IClassFactory;
+ { fields }
+ RefCount: LongInt;
+ end;
+
+function TClassFactoryRec_QueryInterface(Self: PClassFactoryRec; const IID: TIID; var Obj)
+ : HResult; stdcall;
+begin
+ Pointer(Obj) := nil;
+ Result := E_NOTIMPL;
+end;
+
+function TClassFactoryRec_AddRef(Self: PClassFactoryRec): LongInt; stdcall;
+begin
+ inc(Self^.RefCount);
+ Result := Self^.RefCount;
+end;
+
+function TClassFactoryRec_Release(Self: PClassFactoryRec): LongInt; stdcall;
+begin
+ dec(Self^.RefCount);
+ Result := Self^.RefCount;
+ if Result = 0 then
+ begin
+ Dispose(Self);
+ dec(dllpublic.FactoryCount);
+ end; { if }
+end;
+
+function TClassFactoryRec_CreateInstance(Self: PClassFactoryRec; unkOuter: Pointer;
+ const IID: TIID; var Obj): HResult; stdcall;
+var
+ ShlComRec: PShlComRec;
+begin
+ Pointer(Obj) := nil;
+ Result := CLASS_E_NOAGGREGATION;
+ if unkOuter = nil then
+ begin
+ { Before Vista, the system queried for a IShell interface then queried for a context menu, Vista now
+ queries for a context menu (or a shell menu) then QI()'s the other interface }
+ if IsEqualIID(IID, IID_IContextMenu) then
+ begin
+ Result := S_OK;
+ ShlComRec := TShlComRec_Create;
+ Pointer(Obj) := @ShlComRec^.ContextMenu3_Interface;
+ end;
+ if IsEqualIID(IID, IID_IShellExtInit) then
+ begin
+ Result := S_OK;
+ ShlComRec := TShlComRec_Create;
+ Pointer(Obj) := @ShlComRec^.ShellExtInit_Interface;
+ end; // if
+ end; // if
+end;
+
+function TClassFactoryRec_LockServer(Self: PClassFactoryRec; fLock: BOOL): HResult; stdcall;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TClassFactoryRec_Create: PClassFactoryRec;
+begin
+ New(Result);
+ Result^.ptrVTable := @Result^.vTable;
+ { IUnknown }
+ Result^.vTable.QueryInterface := @TClassFactoryRec_QueryInterface;
+ Result^.vTable.AddRef := @TClassFactoryRec_AddRef;
+ Result^.vTable.Release := @TClassFactoryRec_Release;
+ { IClassFactory }
+ Result^.vTable.CreateInstance := @TClassFactoryRec_CreateInstance;
+ Result^.vTable.LockServer := @TClassFactoryRec_LockServer;
+ { inital the variables }
+ Result^.RefCount := 1;
+ { count the number of factories }
+ inc(dllpublic.FactoryCount);
+end;
+
+//
+// IPC part
+//
+
+type
+ PFileList = ^TFileList;
+ TFileList = array [0 .. 0] of PChar;
+ PAddArgList = ^TAddArgList;
+
+ TAddArgList = record
+ szFile: PChar; // file being processed
+ cch: Cardinal; // it's length (with space for NULL char)
+ count: Cardinal; // number we have so far
+ files: PFileList;
+ hContact: THandle;
+ hEvent: THandle;
+ end;
+
+function AddToList(var args: TAddArgList): LongBool;
+var
+ attr: Cardinal;
+ p: Pointer;
+ hFind: THandle;
+ fd: TWIN32FINDDATA;
+ szBuf: array [0 .. MAX_PATH] of Char;
+ szThis: PChar;
+ cchThis: Cardinal;
+begin
+ Result := False;
+ attr := GetFileAttributes(args.szFile);
+ if (attr <> $FFFFFFFF) and ((attr and FILE_ATTRIBUTE_HIDDEN) = 0) then
+ begin
+ if args.count mod 10 = 5 then
+ begin
+ if CallService(MS_SYSTEM_TERMINATED, 0, 0) <> 0 then
+ begin
+ Result := True;
+ Exit;
+ end; // if
+ end;
+ if attr and FILE_ATTRIBUTE_DIRECTORY <> 0 then
begin
- Result := E_INVALIDARG; - // get the contact information - mii.cbSize := sizeof(TMenuItemInfo); - mii.fMask := MIIM_ID or MIIM_DATA; - if GetMenuItemInfo(Self^.hRootMenu, Self^.idCmdFirst + idxCmd, False, mii) then - begin - // get the pointer - Integer(psd) := mii.dwItemData; - // the ID stored in the item pointer and the ID for the menu must match - if (psd = nil) or (psd^.wID <> mii.wID) then + // add the directory
+ lstrcpya(szBuf, args.szFile);
+ ReAllocMem(args.files, (args.count + 1) * sizeof(PChar));
+ GetMem(p, strlen(szBuf) + 1);
+ lstrcpya(p, szBuf);
+ args.files^[args.count] := p;
+ inc(args.count);
+ // tack on ending search token
+ lstrcata(szBuf, '\*');
+ hFind := FindFirstFile(szBuf, fd);
+ while True do
+ begin
+ if fd.cFileName[0] <> '.' then
+ begin
+ lstrcpya(szBuf, args.szFile);
+ lstrcata(szBuf, '\');
+ lstrcata(szBuf, fd.cFileName);
+ // keep a copy of the current thing being processed
+ szThis := args.szFile;
+ args.szFile := szBuf;
+ cchThis := args.cch;
+ args.cch := strlen(szBuf) + 1;
+ // recurse
+ Result := AddToList(args);
+ // restore
+ args.szFile := szThis;
+ args.cch := cchThis;
+ if Result then
+ break;
+ end; // if
+ if not FindNextFile(hFind, fd) then
+ break;
+ end; // while
+ FindClose(hFind);
+ end
+ else
+ begin
+ // add the file
+ ReAllocMem(args.files, (args.count + 1) * sizeof(PChar));
+ GetMem(p, args.cch);
+ lstrcpya(p, args.szFile);
+ args.files^[args.count] := p;
+ inc(args.count);
+ end; // if
+ end;
+end;
+
+procedure MainThreadIssueTransfer(p: PAddArgList); stdcall;
+{$DEFINE SHL_IDC}
+{$DEFINE SHL_KEYS}
+{$INCLUDE shlc.inc}
+{$UNDEF SHL_KEYS}
+{$UNDEF SHL_IDC}
+begin
+ DBWriteContactSettingByte(p^.hContact, SHLExt_Name, SHLExt_MRU, 1);
+ CallService(MS_FILE_SENDSPECIFICFILES, p^.hContact, lParam(p^.files));
+ SetEvent(p^.hEvent);
+end;
+
+function IssueTransferThread(pipch: PHeaderIPC): Cardinal; stdcall;
+var
+ szBuf: array [0 .. MAX_PATH] of Char;
+ pct: PSlotIPC;
+ args: TAddArgList;
+ bQuit: LongBool;
+ j, c: Cardinal;
+ p: Pointer;
+ hMainThread: THandle;
+begin
+ Thread_Push(0,0);
+ hMainThread := THandle(pipch^.Param);
+ GetCurrentDirectory(sizeof(szBuf), szBuf);
+ args.count := 0;
+ args.files := nil;
+ pct := pipch^.DataPtr;
+ bQuit := False;
+ while pct <> nil do
+ begin
+ if (pct^.cbSize <> sizeof(TSlotIPC)) then
+ break;
+ args.szFile := PChar(Integer(pct) + sizeof(TSlotIPC));
+ args.hContact := pct^.hContact;
+ args.cch := pct^.cbStrSection + 1;
+ bQuit := AddToList(args);
+ if bQuit then
+ break;
+ pct := pct^.Next;
+ end; // while
+ if args.files <> nil then
+ begin
+ ReAllocMem(args.files, (args.count + 1) * sizeof(PChar));
+ args.files^[args.count] := nil;
+ inc(args.count);
+ if (not bQuit) then
+ begin
+ args.hEvent := CreateEvent(nil, True, False, nil);
+ QueueUserAPC(@MainThreadIssueTransfer, hMainThread, DWORD(@args));
+ while True do
+ begin
+ if WaitForSingleObjectEx(args.hEvent, INFINITE, True) <> WAIT_IO_COMPLETION then
+ break;
+ end;
+ CloseHandle(args.hEvent);
+ end; // if
+ c := args.count - 1;
+ for j := 0 to c do
+ begin
+ p := args.files^[j];
+ if p <> nil then
+ FreeMem(p);
+ end;
+ FreeMem(args.files);
+ end;
+ SetCurrentDirectory(szBuf);
+ FreeMem(pipch);
+ CloseHandle(hMainThread);
+ Thread_Pop();
+ ExitThread(0);
+end;
+
+type
+
+ PSlotInfo = ^TSlotInfo;
+
+ TSlotInfo = record
+ hContact: THandle;
+ hProto: Cardinal;
+ dwStatus: Integer; // will be aligned anyway
+ end;
+
+ TSlotArray = array [0 .. $FFFFFF] of TSlotInfo;
+ PSlotArray = ^TSlotArray;
+
+function SortContact(var Item1, Item2: TSlotInfo): Integer; stdcall;
+begin
+ Result := CallService(MS_CLIST_CONTACTSCOMPARE, Item1.hContact, Item2.hContact);
+end;
+
+// from FP FCL
+
+procedure QuickSort(FList: PSlotArray; L, R: LongInt);
+var
+ i, j: LongInt;
+ p, q: TSlotInfo;
+begin
+ repeat
+ i := L;
+ j := R;
+ p := FList^[(L + R) div 2];
+ repeat
+ while SortContact(p, FList^[i]) > 0 do
+ inc(i);
+ while SortContact(p, FList^[j]) < 0 do
+ dec(j);
+ if i <= j then
+ begin
+ q := FList^[i];
+ FList^[i] := FList^[j];
+ FList^[j] := q;
+ inc(i);
+ dec(j);
+ end; // if
+ until i > j;
+ if L < j then
+ QuickSort(FList, L, j);
+ L := i;
+ until i >= R;
+end;
+
+{$DEFINE SHL_KEYS}
+{$INCLUDE shlc.inc}
+{$UNDEF SHL_KEYS}
+
+procedure ipcGetSkinIcons(ipch: PHeaderIPC);
+var
+ protoCount: Integer;
+ pp: ^PPROTOCOLDESCRIPTOR;
+ spi: TSlotProtoIcons;
+ j: Cardinal;
+ pct: PSlotIPC;
+ szTmp: array [0 .. 63] of Char;
+ dwCaps: Cardinal;
+begin
+ if (CallService(MS_PROTO_ENUMPROTOCOLS, wParam(@protoCount), lParam(@pp)) = 0) and
+ (protoCount <> 0) then
+ begin
+ spi.pid := GetCurrentProcessId();
+ while protoCount > 0 do
+ begin
+ if (pp^._type = PROTOTYPE_PROTOCOL) then
+ begin
+ lstrcpya(szTmp, pp^.szName);
+ lstrcata(szTmp, PS_GETCAPS);
+ dwCaps := CallService(szTmp, PFLAGNUM_1, 0);
+ if (dwCaps and PF1_FILESEND) <> 0 then
+ begin
+ pct := ipcAlloc(ipch, sizeof(TSlotProtoIcons));
+ if pct <> nil then
+ begin
+ // capture all the icons!
+ spi.hProto := StrHash(pp^.szName);
+ for j := 0 to 9 do
begin
- //MessageBox(0,'ptr assocated with menu is NULL','',MB_OK); - Exit; - end; //if - end else begin
- //MessageBox(0,'GetMenuItemInfo failed?','',MB_OK); - // couldn't get the info, can't start the transfer - Result := E_INVALIDARG; Exit; - end; //if - // is there an IDataObject instance? - if Self^.pDataObject <> nil then - begin - // OpenEvent() the work object to see if the instance is still around - hTransfer := OpenEvent(EVENT_ALL_ACCESS, False, PChar(CreateProcessUID(psd^.pid))); - if hTransfer <> 0 then - begin - // map the ipc file again - hMap := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, IPC_PACKET_SIZE, IPC_PACKET_NAME); - if (hMap <> 0) and (GetLastError <> ERROR_ALREADY_EXISTS) then - begin - // map it to process - pipch := MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); - if pipch <> nil then - begin - // create the name of the object to be signalled by the ST - strcpy(pipch^.SignalEventName, PChar(CreateUID())); - // create it - hReply := CreateEvent(nil, False, False, pipch^.SignalEventName); - if hReply <> 0 then - begin
- if dtCommand in psd^.fTypes then
- begin
- if Assigned(psd^.MenuCommandCallback) then
- Result := psd^.MenuCommandCallback(pipch, hTransfer, hReply, psd);
- end
- else
- begin
- - // prepare the buffer - ipcPrepareRequests(IPC_PACKET_SIZE, pipch, REQUEST_XFRFILES); - // get all the files into the packet - if ipcGetFiles(pipch, Self^.pDataObject, psd^.hContact) = S_OK then - begin - // need to wait for the ST to open the mapping object - // since if we close it before it's opened it the data it - // has will be undefined - replyBits := ipcSendRequest(hTransfer, hReply, pipch, 200); - if replyBits <> REPLY_FAIL then - begin - // they got the files! - Result := S_OK; - end; //if - end;
-
- end; - // close the work object name - CloseHandle(hReply); - end; //if - // unmap it from this process - UnmapViewOfFile(pipch); - end; //if - // close the map - CloseHandle(hMap); - end; //if - // close the handle to the ST object name - CloseHandle(hTransfer); - end; //if - end //if; - end; - - function TShlComRec_InvokeCommand(Self: PContextMenu3_Interface; - var lpici: TCMInvokeCommandInfo): HResult; stdcall; - begin - Result := RequestTransfer(Self^.ptrInstance, LOWORD(Integer(lpici.lpVerb))); - end; - - function TShlComRec_HandleMenuMsgs(Self: PContextMenu3_Interface; - uMsg: UINT; wParam: WPARAM; lParam: LPARAM; pResult: PLResult): HResult; - const - WM_DRAWITEM = $002B; - WM_MEASUREITEM = $002C; - var - dwi: PDrawItemStruct; - msi: PMeasureItemStruct; - psd: PMenuDrawInfo; - ncm: TNonClientMetrics; - hOldFont: THandle; - hFont: THandle; - tS: TSize; - dx: Integer; - hBr: HBRUSH; - icorc: TRect; - hMemDC: HDC; - begin - pResult^ := Integer(True); - if (uMsg = WM_DRAWITEM) and (wParam = 0) then - begin - // either a main sub menu, a group menu or a contact - dwi := PDrawItemStruct(lParam); - Integer(psd) := dwi^.itemData; - // don't fill - SetBkMode(dwi^.hDC, TRANSPARENT); - // where to draw the icon? - icorc.Left := 0; - // center it - with dwi^ do - icorc.Top := rcItem.Top + ((rcItem.Bottom - rcItem.Top) div 2) - (16 div 2); - icorc.Right := icorc.Left + 16; - icorc.Bottom := icorc.Top + 16; - // draw for groups - if (dtGroup in psd^.fTypes) or (dtEntry in psd^.fTypes) then - begin - hBr := GetSysColorBrush(COLOR_MENU); - FillRect(dwi^.hDC, dwi^.rcItem, hBr); - DeleteObject(hBr); - // - if (ODS_SELECTED and dwi^.itemState = ODS_SELECTED) then - begin - // only do this for entry menu types otherwise a black mask - // is drawn under groups - hBr := GetSysColorBrush(COLOR_HIGHLIGHT); - FillRect(dwi^.hDC, dwi^.rcItem, hBr); - DeleteObject(hBr); - SetTextColor(dwi^.hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); - end; //if - // draw icon - with dwi^, icorc do - begin - if (ODS_SELECTED and dwi^.itemState) = ODS_SELECTED then - begin - hBr := GetSysColorBrush(COLOR_HIGHLIGHT); - end else begin - hBr := GetSysColorBrush(COLOR_MENU); - end; //if - DrawIconEx(hDC, Left+1, Top, psd^.hStatusIcon, - 16, 16, // width, height - 0, // step - hBr, // brush - DI_NORMAL); - DeleteObject(hBr); - end; //with - // draw the text - with dwi^ do - begin - Inc(rcItem.Left, ((rcItem.Bottom-rcItem.Top)-2)); - DrawText(hDC, psd^.szText, psd^.cch, rcItem, DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER); - // draw the name of the database text if it's there - if psd^.szProfile <> nil then - begin - GetTextExtentPoint32(dwi^.hDC, psd^.szText, psd^.cch, tS); - Inc(rcItem.Left, tS.cx+8); - SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT)); - DrawText(hDC, psd^.szProfile, lstrlen(psd^.szProfile), rcItem, DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER); - end; //if - end; //with - end else - begin - // it's a contact! - hBr := GetSysColorBrush(COLOR_MENU); - FillRect(dwi^.hDC, dwi^.rcItem, hBr); - DeleteObject(hBr); - if ODS_SELECTED and dwi^.itemState = ODS_SELECTED then - begin - hBr := GetSysColorBrush(COLOR_HIGHLIGHT); - FillRect(dwi^.hDC, dwi^.rcItem, hBr); - DeleteObject(hBr); - SetTextColor(dwi^.hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); - end; - // draw icon - with dwi^, icorc do - begin - if (ODS_SELECTED and dwi^.itemState) = ODS_SELECTED then - begin - hBr := GetSysColorBrush(COLOR_HIGHLIGHT); - end else begin - hBr := GetSysColorBrush(COLOR_MENU); - end; //if - DrawIconEx(hDC, Left+2, Top, psd^.hStatusIcon, - 16, 16, // width, height - 0, // step - hBr, // brush - DI_NORMAL); - DeleteObject(hBr); - end; //with - // draw the text - with dwi^ do - begin - Inc(rcItem.Left, (rcItem.Bottom-rcItem.Top) + 1); - DrawText(hDC, psd^.szText, psd^.cch, rcItem, DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER); - end; //with - end; //if - end - else if (uMsg = WM_MEASUREITEM) then - begin - // don't check if it's really a menu - msi := PMeasureItemStruct(lParam); - Integer(psd) := msi^.itemData; - ncm.cbSize := sizeof(TNonClientMetrics); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @ncm, 0); - // create the font used in menus, this font should be cached somewhere really - {$IFDEF FPC} - hFont := CreateFontIndirect(@ncm.lfMenuFont); - {$ELSE} - hFont := CreateFontIndirect(ncm.lfMenuFont); - {$ENDIF} - hMemDC := Self^.ptrInstance^.hMemDC; - // select in the font - hOldFont := SelectObject(hMemDC, hFont); - // default to an icon - dx := 16; - // get the size 'n' account for the icon - GetTextExtentPoint32(hMemDC, psd^.szText, psd^.cch, tS); - Inc(dx, tS.cx); - // main menu item? - if psd^.szProfile <> nil then - begin - GetTextExtentPoint32(hMemDC, psd^.szProfile, lstrlen(psd^.szProfile), tS); - Inc(dx, tS.cx); - end; - // store it - msi^.itemWidth := dx + Integer(ncm.iMenuWidth); - msi^.itemHeight := Integer(ncm.iMenuHeight)+2; - if tS.cy > msi^.itemHeight then Inc(msi^.itemHeight, tS.cy - msi^.itemHeight); - // clean up - SelectObject(hMemDC, hOldFont); - DeleteObject(hFont); - end; - Result := S_OK; - end; - - function TShlComRec_HandleMenuMsg(Self: PContextMenu3_Interface; - uMsg: UINT; wParam: WPARAM; lParam: LPARAM): HResult; stdcall; - var - Dummy: HResult; - begin - Result := TShlComRec_HandleMenuMsgs(Self, uMsg, wParam, lParam, @Dummy); - end; - - function TShlComRec_HandleMenuMsg2(Self: PContextMenu3_Interface; - uMsg: UINT; wParam: WPARAM; lParam: LPARAM; plResult: Pointer{^LResult}): HResult; stdcall; - var - Dummy: HResult; - begin - // this will be null if a return value isn't needed. - if plResult = nil then plResult := @Dummy; - Result := TShlComRec_HandleMenuMsgs(Self, uMsg, wParam, lParam, plResult); - end; - - function TShlComRec_Create: PShlComRec; - var - DC: HDC; - begin - New(Result); - { build all the function tables for interfaces } - with Result^.ShellExtInit_Interface do - begin - { this is only owned by us... } - ptrVTable := @vTable; - { IUnknown } - vTable.QueryInterface := @TShlComRec_QueryInterface; - vTable.AddRef := @TShlComRec_AddRef; - vTable.Release := @TShlComRec_Release; - { IShellExtInit } - vTable.Initialise := @TShlComRec_Initialise; - { instance of a TShlComRec } - ptrInstance := Result; - end; - with Result^.ContextMenu3_Interface do - begin - ptrVTable := @vTable; - { IUnknown } - vTable.QueryInterface := @TShlComRec_QueryInterface; - vTable.AddRef := @TShlComRec_AddRef; - vTable.Release := @TShlComRec_Release; - { IContextMenu } - vTable.QueryContextMenu := @TShlComRec_QueryContextMenu; - vTable.InvokeCommand := @TShlComRec_InvokeCommand; - vTable.GetCommandString := @TShlComRec_GetCommandString; - { IContextMenu2 } - vTable.HandleMenuMsg := @TShlComRec_HandleMenuMsg; - { IContextMenu3 } - vTable.HandleMenuMsg2 := @TShlComRec_HandleMenuMsg2; - { instance data } - ptrInstance := Result; - end; - { initalise variables } - Result^.RefCount := 1; - Result^.hDllHeap := HeapCreate(0, 0, 0); - Result^.hRootMenu := 0;
- Result^.hRecentMenu := 0;
- Result^.RecentCount := 0; - Result^.idCmdFirst := 0; - Result^.pDataObject := nil; - Result^.ProtoIcons := nil; - Result^.ProtoIconsCount := 0; - // create an inmemory DC - DC := GetDC(0); - Result^.hMemDC := CreateCompatibleDC(DC); - ReleaseDC(0,DC); - { keep count on the number of objects } - Inc(dllpublic.ObjectCount); - end; - -{ IClassFactory } - -type - - PVTable_IClassFactory = ^TVTable_IClassFactory; - TVTable_IClassFactory = record - { IUnknown } - QueryInterface: Pointer; - AddRef: Pointer; - Release: Pointer; - { IClassFactory } - CreateInstance: Pointer; - LockServer: Pointer; - end; - PClassFactoryRec = ^TClassFactoryRec; - TClassFactoryRec = record - ptrVTable: PVTable_IClassFactory; - vTable: TVTable_IClassFactory; - {fields} - RefCount: LongInt; - end; - - function TClassFactoryRec_QueryInterface(Self: PClassFactoryRec; - const IID: TIID; var Obj): HResult; stdcall; - begin - Pointer(Obj) := nil; - Result := E_NOTIMPL; - end; - - function TClassFactoryRec_AddRef(Self: PClassFactoryRec): LongInt; stdcall; - begin - Inc(Self^.RefCount); - Result := Self^.RefCount; - end; - - function TClassFactoryRec_Release(Self: PClassFactoryRec): LongInt; stdcall; - begin - Dec(Self^.RefCount); - Result := Self^.RefCount; - if Result = 0 then - begin - Dispose(Self); - Dec(dllpublic.FactoryCount); - end; {if} - end; - - function TClassFactoryRec_CreateInstance(Self: PClassFactoryRec; - unkOuter: Pointer; const IID: TIID; var Obj): HResult; stdcall; - var - ShlComRec: PShlComRec; - begin - Pointer(Obj) := nil; - Result := CLASS_E_NOAGGREGATION; - if unkOuter = nil then - begin - { Before Vista, the system queried for a IShell interface then queried for a context menu, Vista now - queries for a context menu (or a shell menu) then QI()'s the other interface } - if IsEqualIID(IID, IID_IContextMenu) then - begin - Result := S_OK; - ShlComRec := TShlComRec_Create; - Pointer(Obj) := @ShlComRec^.ContextMenu3_Interface; - end; - if IsEqualIID(IID, IID_IShellExtInit) then - begin - Result := S_OK; - ShlComRec := TShlComRec_Create; - Pointer(Obj) := @ShlComRec^.ShellExtInit_Interface; - end; //if - end; //if - end; - function TClassFactoryRec_LockServer(Self: PClassFactoryRec; fLock: BOOL): HResult; stdcall; - begin - Result := E_NOTIMPL; - end; - - function TClassFactoryRec_Create: PClassFactoryRec; - begin - New(Result); - Result^.ptrVTable := @Result^.vTable; - { IUnknown } - Result^.vTable.QueryInterface := @TClassFactoryRec_QueryInterface; - Result^.vTable.AddRef := @TClassFactoryRec_AddRef; - Result^.vTable.Release := @TClassFactoryRec_Release; - { IClassFactory } - Result^.vTable.CreateInstance := @TClassFactoryRec_CreateInstance; - Result^.vTable.LockServer := @TClassFactoryRec_LockServer; - { inital the variables } - Result^.RefCount := 1; - { count the number of factories } - Inc(dllpublic.FactoryCount); - end; - - // - // IPC part - // - - - type - PFileList = ^TFileList; - TFileList = array[0..0] of PChar; - PAddArgList = ^TAddArgList; - TAddArgList = record - szFile: PChar; // file being processed - cch: Cardinal; // it's length (with space for NULL char) - count: Cardinal; // number we have so far - files: PFileList; - hContact: THandle; - hEvent: THandle; - end; - - function AddToList(var args: TAddArgList): LongBool; - var - attr: Cardinal; - p: Pointer; - hFind: THandle; - fd: TWIN32FINDDATA; - szBuf: array[0..MAX_PATH] of Char; - szThis: PChar; - cchThis: Cardinal; - begin - Result := False; - attr := GetFileAttributes(args.szFile); - if (attr <> $FFFFFFFF) and ((attr and FILE_ATTRIBUTE_HIDDEN) = 0) then - begin - if args.count mod 10 = 5 then - begin - if CallService(MS_SYSTEM_TERMINATED,0,0) <> 0 then - begin - Result := True; Exit; - end; //if - end; - if attr and FILE_ATTRIBUTE_DIRECTORY <> 0 then - begin - // add the directory - strcpy(szBuf,args.szFile); - ReallocMem(args.files,(args.count+1)*sizeof(PChar)); - GetMem(p,strlen(szBuf)+1); - strcpy(p,szBuf); - args.files^[args.count] := p; - inc(args.count); - // tack on ending search token - strcat(szBuf,'\*'); - hFind := FindFirstFile(szBuf,fd); - while True do - begin - if fd.cFileName[0] <> '.' then - begin - strcpy(szBuf,args.szFile); - strcat(szBuf,'\'); - strcat(szBuf,fd.cFileName); - // keep a copy of the current thing being processed - szThis := args.szFile; args.szFile := szBuf; - cchThis := args.cch; args.cch := strlen(szBuf)+1; - // recurse - Result := AddToList(args); - // restore - args.szFile := szThis; - args.cch := cchThis; - if Result then Break; - end; //if - if not FindNextFile(hFind,fd) then Break; - end; //while - FindClose(hFind); - end else begin - // add the file - ReallocMem(args.files,(args.count+1)*sizeof(PChar)); - GetMem(p,args.cch); - strcpy(p,args.szFile); - args.files^[args.count] := p; - inc (args.count); - end; //if - end; - end; - - procedure MainThreadIssueTransfer(p: PAddArgList); stdcall;
- {$define SHL_IDC}
- {$define SHL_KEYS}
- {$include shlc.inc}
- {$undef SHL_KEYS}
- {$undef SHL_IDC} - begin
- DBWriteContactSettingByte(p^.hContact, SHLExt_Name, SHLExt_MRU, 1); - CallService(MS_FILE_SENDSPECIFICFILES,p^.hContact,lParam(p^.files)); - SetEvent(p^.hEvent); - end; - - function IssueTransferThread(pipch: PHeaderIPC): Cardinal; stdcall; - var - szBuf: array[0..MAX_PATH] of Char; - pct: PSlotIPC; - args: TAddArgList; - bQuit: LongBool; - j,c: Cardinal; - p: Pointer; - hMainThread: THandle; - begin - CallService(MS_SYSTEM_THREAD_PUSH,0,0); - hMainThread := THandle(pipch^.Param); - GetCurrentDirectory(sizeof(szBuf),szBuf); - args.count := 0; - args.files := nil; - pct := pipch^.DataPtr; - bQuit := False; - while pct <> nil do - begin - if (pct^.cbSize <> sizeof(TSlotIPC)) then break; - args.szFile := PChar(Integer(pct) + sizeof(TSlotIPC)); - args.hContact := pct^.hContact; - args.cch := pct^.cbStrSection+1; - bQuit := AddToList(args); - if bQuit then Break; - pct := pct^.next; - end; //while - if args.files <> nil then - begin - ReallocMem(args.files,(args.count+1)*sizeof(PChar)); - args.files^[args.count] := nil; - inc (args.count); - if (not bQuit) then - begin - args.hEvent := CreateEvent(nil, True, False, nil); - QueueUserAPC(@MainThreadIssueTransfer,hMainThread,DWORD(@args)); - while True do - begin - if WaitForSingleObjectEx(args.hEvent,INFINITE,True) <> WAIT_IO_COMPLETION then Break; - end; - CloseHandle(args.hEvent); - end; //if - c := args.count-1; - for j := 0 to c do - begin - p := args.files^[j]; - if p <> nil then FreeMem(p); - end; - FreeMem(args.files); - end; - SetCurrentDirectory(szBuf); - FreeMem(pipch); - CloseHandle(hMainThread); - CallService(MS_SYSTEM_THREAD_POP,0,0); - ExitThread(0); - end; - -type - - PSlotInfo = ^TSlotInfo; - TSlotInfo = record - hContact: THandle; - hProto: Cardinal; - dwStatus: Integer; // will be aligned anyway - end; - TSlotArray = array[0..$FFFFFF] of TSlotInfo; - PSlotArray = ^TSlotArray; - - function SortContact(var Item1, Item2: TSlotInfo): Integer; stdcall; - begin - Result := PluginLink^.CallService(MS_CLIST_CONTACTSCOMPARE, item1.hContact, item2.hContact); - end; - - // from FP FCL - - procedure QuickSort (FList: PSlotArray; L, R: LongInt); - var - I, J: LongInt; - P, Q: TSlotInfo; - begin - repeat - I := L; - J := R; - P := FList^[ (L+R) div 2 ]; - repeat - while SortContact(P, FList^[i]) > 0 do Inc(i); - while SortContact(P, FList^[j]) < 0 do Dec(j); - if I <= J then - begin - Q := FList^[I]; - FList^[I] := FList^[J]; - FList^[J] := Q; - Inc(I); - Dec(J); - end; //if - until I > J; - if L < J then QuickSort (FList, L, J); - L := I; - until I >= R; - end; - - {$define SHL_KEYS} - {$include shlc.inc} - {$undef SHL_KEYS} - - procedure ipcGetSkinIcons(ipch: PHeaderIPC); - var - protoCount: Integer; - pp: ^PPROTOCOLDESCRIPTOR; - spi: TSlotProtoIcons; - j: Cardinal; - pct: PSlotIPC; - szTmp: array[0..63] of Char; - dwCaps: Cardinal; - begin - if (CallService(MS_PROTO_ENUMPROTOCOLS,wParam(@protoCount),lParam(@pp)) = 0) and (protoCount <> 0) then - begin - spi.pid := GetCurrentProcessId(); - while protoCount > 0 do - begin - if (pp^.type_ = PROTOTYPE_PROTOCOL) then - begin - strcpy(szTmp,pp^.szName); - strcat(szTmp,PS_GETCAPS); - dwCaps := CallService(szTmp,PFLAGNUM_1,0); - if (dwCaps and PF1_FILESEND) <> 0 then - begin - pct := ipcAlloc(ipch,sizeof(TSlotProtoIcons)); - if pct <> nil then - begin - // capture all the icons! - spi.hProto := StrHash(pp^.szName); - for j := 0 to 9 do - begin - spi.hIcons[j] := LoadSkinnedProtoIcon(pp^.szName,ID_STATUS_OFFLINE+j); - end; //for - pct^.fType := REQUEST_NEWICONS; - CopyMemory(Pointer(Integer(pct)+sizeof(TSlotIPC)),@spi,sizeof(TSlotProtoIcons)); - if ipch^.NewIconsBegin = nil then ipch^.NewIconsBegin := pct; - end; //if - end; //if - end; //if - inc (pp); - dec (protoCount); - end; //while - end; //if - // add Miranda icon - pct := ipcAlloc(ipch, sizeof(TSlotProtoIcons)); - if pct <> nil then - begin - ZeroMemory(@spi.hIcons, sizeof(spi.hIcons)); - spi.hProto := 0; // no protocol - spi.hIcons[0] := LoadSkinnedIcon(SKINICON_OTHER_MIRANDA); - pct^.fType := REQUEST_NEWICONS; - CopyMemory(Pointer(Integer(pct)+sizeof(TSlotIPC)),@spi,sizeof(TSlotProtoIcons)); - if ipch^.NewIconsBegin = nil then ipch^.NewIconsBegin := pct; - end; //if - end; - - function ipcGetSortedContacts(ipch: PHeaderIPC; pSlot: pint; bGroupMode: Boolean): Boolean; - var - dwContacts: Cardinal; - pContacts: PSlotArray; - hContact: THandle; - I: Integer; - dwOnline: Cardinal; - szProto: PChar; - dwStatus: Integer; - pct: PSlotIPC; - szContact: PChar; - dbv: TDBVariant; - bHideOffline: Boolean; - szTmp: array[0..63] of Char; - dwCaps: Cardinal; - szSlot: PChar; - n, rc, cch: Cardinal; - begin - Result := False; - // hide offliners? - bHideOffline := DBGetContactSettingByte(0, 'CList', 'HideOffline', 0) = 1; - // do they wanna hide the offline people anyway? - if DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoOffline, 0) = 1 then - begin - // hide offline people - bHideOffline := True; - end; - // get the number of contacts - dwContacts := PluginLink^.CallService(MS_DB_CONTACT_GETCOUNT, 0, 0); - if dwContacts = 0 then Exit; - // get the contacts in the array to be sorted by status, trim out anyone - // who doesn't wanna be seen. - GetMem(pContacts, (dwContacts+2) * sizeof(TSlotInfo)); - i := 0; - dwOnline := 0; - hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); - while (hContact <> 0) do - begin - if i >= dwContacts then Break; - (* do they have a running protocol? *) - Integer(szProto) := PluginLink^.CallService(MS_PROTO_GETCONTACTBASEPROTO, hContact, 0); - if szProto <> nil then - begin - (* does it support file sends? *) - strcpy(szTmp, szProto); - strcat(szTmp, PS_GETCAPS); - dwCaps := CallService(szTmp,PFLAGNUM_1,0); - if (dwCaps and PF1_FILESEND) = 0 then - begin - hContact := CallService(MS_DB_CONTACT_FINDNEXT,hContact,0); - continue; - end; - dwStatus := DBGetContactSettingWord(hContact, szProto, 'Status', ID_STATUS_OFFLINE); - if dwStatus <> ID_STATUS_OFFLINE then Inc(dwOnline) - else if bHideOffline then - begin - hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0); - continue; - end; //if - // is HIT on? - if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHITContacts, BST_UNCHECKED) then - begin - // don't show people who are "Hidden" "NotOnList" or Ignored - if (DBGetContactSettingByte(hContact, 'CList', 'Hidden', 0) = 1) - or (DBGetContactSettingByte(hContact, 'CList', 'NotOnList', 0) = 1) - or (PluginLink^.CallService(MS_IGNORE_ISIGNORED, hContact, IGNOREEVENT_MESSAGE or IGNOREEVENT_URL or IGNOREEVENT_FILE) <> 0) then - begin - hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0); - continue; - end; //if - end; //if - // is HIT2 off? - if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHIT2Contacts, BST_UNCHECKED) then - begin - if DBGetContactSettingWord(hContact, szProto, 'ApparentMode', 0) = ID_STATUS_OFFLINE then - begin - hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0); - continue; - end; //if - end; //if - // store - pContacts^[i].hContact := hContact; - pContacts^[i].dwStatus := dwStatus; - pContacts^[i].hProto := StrHash(szProto); - Inc(i); - end else begin - // contact has no protocol! - end; //if - hContact := PluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0); - end; //while - // if no one is online and the CList isn't showing offliners, quit - if (dwOnline = 0) and (bHideOffline) then - begin - FreeMem(pContacts); Exit; - end; //if - dwContacts := i; i := 0; - // sort the array - QuickSort(pContacts, 0, dwContacts-1); - // create an IPC slot for each contact and store display name, etc - while i < dwContacts do - begin - Integer(szContact) := PluginLink^.CallService(MS_CLIST_GETCONTACTDISPLAYNAME, pContacts^[i].hContact, 0); - if (szContact <> nil) then - begin - n := 0; - rc := 1; - if bGroupMode then - begin - rc := DBGetContactSetting(pContacts^[i].hContact,'CList','Group',@dbv); - if rc = 0 then - begin - n := lstrlen(dbv.pszVal)+1; - end; - end; //if - cch := lstrlen(szContact)+1; - pct := ipcAlloc(ipch, cch + 1 + n); - if pct = nil then - begin - DBFreeVariant(@dbv); - break; - end; - // lie about the actual size of the TSlotIPC - pct^.cbStrSection := cch; - szSlot := PChar(Integer(pct) + sizeof(TSlotIPC)); - strcpy(szSlot, szContact); - pct^.fType := REQUEST_CONTACTS; - pct^.hContact := pContacts^[i].hContact; - pct^.Status := pContacts^[i].dwStatus; - pct^.hProto := pContacts^[i].hProto;
- pct^.MRU := DBGetContactSettingByte(pct^.hContact, SHLExt_Name, SHLExt_MRU, 0); - if ipch^.ContactsBegin = nil then ipch^.ContactsBegin := pct; - inc(szSlot,cch+1); - if rc=0 then - begin - pct^.hGroup := StrHash(dbv.pszVal); - strcpy(szSlot,dbv.pszVal); - DBFreeVariant(@dbv); - end else begin - pct^.hGroup := 0; - szSlot^ := #0; - end; - inc(pSlot^); - end; //if - Inc(i); - end; //while - FreeMem(pContacts); - // - Result := True; + spi.hIcons[j] := LoadSkinnedProtoIcon(pp^.szName, ID_STATUS_OFFLINE + j);
+ end; // for
+ pct^.fType := REQUEST_NEWICONS;
+ CopyMemory(Pointer(Integer(pct) + sizeof(TSlotIPC)), @spi, sizeof(TSlotProtoIcons));
+ if ipch^.NewIconsBegin = nil then
+ ipch^.NewIconsBegin := pct;
+ end; // if
+ end; // if
+ end; // if
+ inc(pp);
+ dec(protoCount);
+ end; // while
+ end; // if
+ // add Miranda icon
+ pct := ipcAlloc(ipch, sizeof(TSlotProtoIcons));
+ if pct <> nil then
+ begin
+ ZeroMemory(@spi.hIcons, sizeof(spi.hIcons));
+ spi.hProto := 0; // no protocol
+ spi.hIcons[0] := LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ pct^.fType := REQUEST_NEWICONS;
+ CopyMemory(Pointer(Integer(pct) + sizeof(TSlotIPC)), @spi, sizeof(TSlotProtoIcons));
+ if ipch^.NewIconsBegin = nil then
+ ipch^.NewIconsBegin := pct;
+ end; // if
+end;
+
+function ipcGetSortedContacts(ipch: PHeaderIPC; pSlot: pint; bGroupMode: Boolean): Boolean;
+var
+ dwContacts: Cardinal;
+ pContacts: PSlotArray;
+ hContact: THandle;
+ i: Integer;
+ dwOnline: Cardinal;
+ szProto: PChar;
+ dwStatus: Integer;
+ pct: PSlotIPC;
+ szContact: PChar;
+ dbv: TDBVariant;
+ bHideOffline: Boolean;
+ szTmp: array [0 .. 63] of Char;
+ dwCaps: Cardinal;
+ szSlot: PChar;
+ n, rc, cch: Cardinal;
+begin
+ Result := False;
+ // hide offliners?
+ bHideOffline := DBGetContactSettingByte(0, 'CList', 'HideOffline', 0) = 1;
+ // do they wanna hide the offline people anyway?
+ if DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoOffline, 0) = 1 then
+ begin
+ // hide offline people
+ bHideOffline := True;
+ end;
+ // get the number of contacts
+ dwContacts := CallService(MS_DB_CONTACT_GETCOUNT, 0, 0);
+ if dwContacts = 0 then
+ Exit;
+ // get the contacts in the array to be sorted by status, trim out anyone
+ // who doesn't wanna be seen.
+ GetMem(pContacts, (dwContacts + 2) * sizeof(TSlotInfo));
+ i := 0;
+ dwOnline := 0;
+ hContact := CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact <> 0) do
+ begin
+ if i >= dwContacts then
+ break;
+ (* do they have a running protocol? *)
+ int_ptr(szProto) := CallService(MS_PROTO_GETCONTACTBASEPROTO, hContact, 0);
+ if szProto <> nil then
+ begin
+ (* does it support file sends? *)
+ lstrcpya(szTmp, szProto);
+ lstrcata(szTmp, PS_GETCAPS);
+ dwCaps := CallService(szTmp, PFLAGNUM_1, 0);
+ if (dwCaps and PF1_FILESEND) = 0 then
+ begin
+ hContact := CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ continue;
+ end;
+ dwStatus := DBGetContactSettingWord(hContact, szProto, 'Status', ID_STATUS_OFFLINE);
+ if dwStatus <> ID_STATUS_OFFLINE then
+ inc(dwOnline)
+ else if bHideOffline then
+ begin
+ hContact := CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ continue;
+ end; // if
+ // is HIT on?
+ if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHITContacts,
+ BST_UNCHECKED) then
+ begin
+ // don't show people who are "Hidden" "NotOnList" or Ignored
+ if (DBGetContactSettingByte(hContact, 'CList', 'Hidden', 0) = 1) or
+ (DBGetContactSettingByte(hContact, 'CList', 'NotOnList', 0) = 1) or
+ (CallService(MS_IGNORE_ISIGNORED, hContact, IGNOREEVENT_MESSAGE or
+ IGNOREEVENT_URL or IGNOREEVENT_FILE) <> 0) then
+ begin
+ hContact := CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ continue;
+ end; // if
+ end; // if
+ // is HIT2 off?
+ if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseHIT2Contacts,
+ BST_UNCHECKED) then
+ begin
+ if DBGetContactSettingWord(hContact, szProto, 'ApparentMode', 0) = ID_STATUS_OFFLINE
+ then
+ begin
+ hContact := CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ continue;
+ end; // if
+ end; // if
+ // store
+ pContacts^[i].hContact := hContact;
+ pContacts^[i].dwStatus := dwStatus;
+ pContacts^[i].hProto := StrHash(szProto);
+ inc(i);
+ end
+ else
+ begin
+ // contact has no protocol!
+ end; // if
+ hContact := CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ end; // while
+ // if no one is online and the CList isn't showing offliners, quit
+ if (dwOnline = 0) and (bHideOffline) then
+ begin
+ FreeMem(pContacts);
+ Exit;
+ end; // if
+ dwContacts := i;
+ i := 0;
+ // sort the array
+ QuickSort(pContacts, 0, dwContacts - 1);
+ // create an IPC slot for each contact and store display name, etc
+ while i < dwContacts do
+ begin
+ int_ptr(szContact) := CallService(MS_CLIST_GETCONTACTDISPLAYNAME,
+ pContacts^[i].hContact, 0);
+ if (szContact <> nil) then
+ begin
+ n := 0;
+ rc := 1;
+ if bGroupMode then
+ begin
+ rc := DBGetContactSetting(pContacts^[i].hContact, 'CList', 'Group', @dbv);
+ if rc = 0 then
+ begin
+ n := lstrlena(dbv.szVal.a) + 1;
+ end;
+ end; // if
+ cch := lstrlena(szContact) + 1;
+ pct := ipcAlloc(ipch, cch + 1 + n);
+ if pct = nil then
+ begin
+ DBFreeVariant(@dbv);
+ break;
+ end;
+ // lie about the actual size of the TSlotIPC
+ pct^.cbStrSection := cch;
+ szSlot := PChar(Integer(pct) + sizeof(TSlotIPC));
+ lstrcpya(szSlot, szContact);
+ pct^.fType := REQUEST_CONTACTS;
+ pct^.hContact := pContacts^[i].hContact;
+ pct^.Status := pContacts^[i].dwStatus;
+ pct^.hProto := pContacts^[i].hProto;
+ pct^.MRU := DBGetContactSettingByte(pct^.hContact, SHLExt_Name, SHLExt_MRU, 0);
+ if ipch^.ContactsBegin = nil then
+ ipch^.ContactsBegin := pct;
+ inc(szSlot, cch + 1);
+ if rc = 0 then
+ begin
+ pct^.hGroup := StrHash(dbv.szVal.a);
+ lstrcpya(szSlot, dbv.szVal.a);
+ DBFreeVariant(@dbv);
+ end
+ else
+ begin
+ pct^.hGroup := 0;
+ szSlot^ := #0;
+ end;
+ inc(pSlot^);
+ end; // if
+ inc(i);
+ end; // while
+ FreeMem(pContacts);
+ //
+ Result := True;
+end;
+
+// worker thread to clear MRU, called by the IPC bridge
+function ClearMRUThread(notused: Pointer): Cardinal; stdcall;
+{$DEFINE SHL_IDC}
+{$DEFINE SHL_KEYS}
+{$INCLUDE shlc.inc}
+{$UNDEF SHL_KEYS}
+{$UNDEF SHL_IDC}
+var
+ hContact: THandle;
+begin
+ Thread_Push(0,0);
+
+ begin
+ hContact := CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while hContact <> 0 do
+ begin
+ if DBGetContactSettingByte(hContact, SHLExt_Name, SHLExt_MRU, 0) > 0 then
+ begin
+ DBWriteContactSettingByte(hContact, SHLExt_Name, SHLExt_MRU, 0);
+ end;
+ hContact := CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
+ end;
+ end;
+ Thread_Pop();
+ ExitThread(0);
+end;
+
+// this function is called from an APC into the main thread
+procedure ipcService(dwParam: DWORD); stdcall;
+label
+ Reply;
+var
+ hMap: THandle;
+ pMMT: PHeaderIPC;
+ hSignal: THandle;
+ pct: PSlotIPC;
+ hContact: THandle;
+ szContact: PChar;
+ Status: int;
+ szBuf: PChar;
+ iSlot: Integer;
+ szGroupStr: array [0 .. 31] of Char;
+ dbv: TDBVariant;
+ bits: pint;
+ HICON: THandle;
+ i: Integer;
+ bGroupMode: Boolean;
+ tid: Cardinal;
+ cloned: PHeaderIPC;
+ szMiranda: PChar;
+begin
+ { try to open the file mapping object the caller must make sure no other
+ running instance is using this file }
+ hMap := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, IPC_PACKET_NAME);
+ If hMap <> 0 then
+ begin
+ { map the file to this process }
+ pMMT := MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ { if it fails the caller should of had some timeout in wait }
+ if (pMMT <> nil) and (pMMT^.cbSize = sizeof(THeaderIPC)) and
+ (pMMT^.dwVersion = PLUGIN_MAKE_VERSION(2, 0, 1, 2)) then
+ begin
+ // toggle the right bits
+ bits := @pMMT^.fRequests;
+ // jump right to a worker thread for file processing?
+ if (bits^ and REQUEST_XFRFILES) = REQUEST_XFRFILES then
+ begin
+ GetMem(cloned, IPC_PACKET_SIZE);
+ // translate from client space to cloned heap memory
+ pMMT^.pServerBaseAddress := pMMT^.pClientBaseAddress;
+ pMMT^.pClientBaseAddress := cloned;
+ CopyMemory(cloned, pMMT, IPC_PACKET_SIZE);
+ ipcFixupAddresses(True, cloned);
+ DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
+ @cloned^.Param, THREAD_SET_CONTEXT, False, 0);
+ CloseHandle(CreateThread(nil, 0, @IssueTransferThread, cloned, 0, tid));
+ goto Reply;
+ end;
+ // the request was to clear the MRU entries, we have no return data
+ if (bits^ and REQUEST_CLEARMRU) = REQUEST_CLEARMRU then
+ begin
+ CloseHandle(CreateThread(nil, 0, @ClearMRUThread, nil, 0, tid));
+ goto Reply;
+ end;
+ // the IPC header may have pointers that need to be translated
+ // in either case the supplied data area pointers has to be
+ // translated to this address space.
+ // the server base address is always removed to get an offset
+ // to which the client base is added, this is what ipcFixupAddresses() does
+ pMMT^.pServerBaseAddress := pMMT^.pClientBaseAddress;
+ pMMT^.pClientBaseAddress := pMMT;
+ // translate to the server space map
+ ipcFixupAddresses(True, pMMT);
+ // store the address map offset so the caller can retranslate
+ pMMT^.pServerBaseAddress := pMMT;
+ // return some options to the client
+ if DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoIcons, 0) <> 0 then
+ begin
+ pMMT^.dwFlags := HIPC_NOICONS;
+ end;
+ // see if we have a custom string for 'Miranda'
+ szMiranda := Translate('Miranda');
+ lstrcpyn(pMMT^.MirandaName, szMiranda, sizeof(pMMT^.MirandaName) - 1);
+
+ // for the MRU menu
+ szBuf := Translate('Recently');
+ lstrcpyn(pMMT^.MRUMenuName, szBuf, sizeof(pMMT^.MRUMenuName) - 1);
+
+ // and a custom string for "clear entries"
+ szBuf := Translate('Clear entries');
+ lstrcpyn(pMMT^.ClearEntries, szBuf, sizeof(pMMT^.ClearEntries) - 1);
+
+ // if the group mode is on, check if they want the CList setting
+ bGroupMode := BST_CHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseGroups,
+ BST_UNCHECKED);
+ if bGroupMode and (BST_CHECKED = DBGetContactSettingByte(0, SHLExt_Name,
+ SHLExt_UseCListSetting, BST_UNCHECKED)) then
+ begin
+ bGroupMode := 1 = DBGetContactSettingByte(0, 'CList', 'UseGroups', 0);
+ end;
+ iSlot := 0;
+ // return profile if set
+ if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoProfile,
+ BST_UNCHECKED) then
+ begin
+ pct := ipcAlloc(pMMT, 50);
+ if pct <> nil then
+ begin
+ // will actually return with .dat if there's space for it, not what the docs say
+ pct^.Status := STATUS_PROFILENAME;
+ CallService(MS_DB_GETPROFILENAME, 49, Integer(pct) + sizeof(TSlotIPC));
+ end; // if
+ end; // if
+ if (bits^ and REQUEST_NEWICONS) = REQUEST_NEWICONS then
+ begin
+ ipcGetSkinIcons(pMMT);
+ end;
+ if (bits^ and REQUEST_GROUPS = REQUEST_GROUPS) then
+ begin
+ // return contact's grouping if it's present
+ while bGroupMode do
+ begin
+ str(iSlot, szGroupStr);
+ if DBGetContactSetting(0, 'CListGroups', szGroupStr, @dbv) <> 0 then
+ break;
+ pct := ipcAlloc(pMMT, lstrlena(dbv.szVal.a + 1) + 1);
+ // first byte has flags, need null term
+ if pct <> nil then
+ begin
+ if pMMT^.GroupsBegin = nil then
+ pMMT^.GroupsBegin := pct;
+ pct^.fType := REQUEST_GROUPS;
+ pct^.hContact := 0;
+ int_ptr(szBuf) := int_ptr(pct) + sizeof(TSlotIPC); // get the end of the slot
+ lstrcpya(szBuf, dbv.szVal.a + 1);
+ pct^.hGroup := 0;
+ DBFreeVariant(@dbv); // free the string
+ end
+ else
+ begin
+ // outta space
+ DBFreeVariant(@dbv);
+ break;
+ end; // if
+ inc(iSlot);
+ end; { while }
+ // if there was no space left, it'll end on null
+ if pct = nil then
+ bits^ := (bits^ or GROUPS_NOTIMPL) and not REQUEST_GROUPS;
+ end; { if: group request }
+ // SHOULD check slot space.
+ if (bits^ and REQUEST_CONTACTS = REQUEST_CONTACTS) then
+ begin
+ if not ipcGetSortedContacts(pMMT, @iSlot, bGroupMode) then
+ begin
+ // fail if there were no contacts AT ALL
+ bits^ := (bits^ or CONTACTS_NOTIMPL) and not REQUEST_CONTACTS;
+ end; // if
+ end; // if:contact request
+ // store the number of slots allocated
+ pMMT^.Slots := iSlot;
+ Reply:
+ { get the handle the caller wants to be signalled on }
+ hSignal := OpenEvent(EVENT_ALL_ACCESS, False, pMMT^.SignalEventName);
+ { did it open? }
+ If hSignal <> 0 then
+ begin
+ { signal and close }
+ SetEvent(hSignal);
+ CloseHandle(hSignal);
+ end;
+ { unmap the shared memory from this process }
+ UnmapViewOfFile(pMMT);
end;
-
- // worker thread to clear MRU, called by the IPC bridge
- function ClearMRUThread(notused: Pointer): Cardinal; stdcall;
- {$define SHL_IDC}
- {$define SHL_KEYS}
- {$include shlc.inc}
- {$undef SHL_KEYS}
- {$undef SHL_IDC}
- var
- hContact: THandle;
- begin
- CallService(MS_SYSTEM_THREAD_PUSH,0,0);
- begin
- hContact := pluginLink^.CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
- while hContact <> 0 do
- begin
- if DBGetContactSettingByte(hContact, SHLExt_Name, SHLExt_MRU, 0) > 0 then
- begin
- DBWriteContactSettingByte(hContact, SHLExt_Name, SHLExt_MRU, 0);
- end;
- hContact := pluginLink^.CallService(MS_DB_CONTACT_FINDNEXT, hContact, 0);
- end;
- end;
- CallService(MS_SYSTEM_THREAD_POP,0,0);
- ExitThread(0);
- end; -
- // this function is called from an APC into the main thread - procedure ipcService(dwParam: DWORD); stdcall; - label - Reply; - var - hMap: THandle; - pMMT: PHeaderIPC; - hSignal: THandle; - pct: PSlotIPC; - hContact: THandle; - szContact: PChar; - Status: int; - szBuf: PChar; - iSlot: Integer; - szGroupStr: array[0..31] of Char; - dbv: TDBVARIANT; - bits: pint; - hIcon: THandle; - I: Integer; - bGroupMode: Boolean; - tid: Cardinal; - cloned: PHeaderIPC; - szMiranda: PChar; - begin - { try to open the file mapping object the caller must make sure no other - running instance is using this file } - hMap := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, IPC_PACKET_NAME); - If hMap <> 0 then - begin - { map the file to this process } - pMMT := MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); - { if it fails the caller should of had some timeout in wait } - if (pMMT <> nil) and (pMMT^.cbSize = sizeof(THeaderIPC)) and (pMMT^.dwVersion = PLUGIN_MAKE_VERSION(2,0,1,2)) then - begin - // toggle the right bits - bits := @pMMT^.fRequests; - // jump right to a worker thread for file processing? - if (bits^ and REQUEST_XFRFILES) = REQUEST_XFRFILES then - begin - GetMem(cloned, IPC_PACKET_SIZE); - // translate from client space to cloned heap memory - pMMT^.pServerBaseAddress := pMMT^.pClientBaseAddress; - pMMT^.pClientBaseAddress := cloned; - CopyMemory(cloned, pMMT, IPC_PACKET_SIZE); - ipcFixupAddresses(True, cloned); - DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),@cloned^.Param,THREAD_SET_CONTEXT,FALSE,0); - CloseHandle(CreateThread(nil,0,@IssueTransferThread,cloned,0,tid)); - goto Reply; - end;
- // the request was to clear the MRU entries, we have no return data
- if (bits^ and REQUEST_CLEARMRU) = REQUEST_CLEARMRU then
- begin
- CloseHandle(CreateThread(nil,0,@ClearMRUThread,nil,0,tid));
- goto reply;
- end; - // the IPC header may have pointers that need to be translated - // in either case the supplied data area pointers has to be - // translated to this address space. - // the server base address is always removed to get an offset - // to which the client base is added, this is what ipcFixupAddresses() does - pMMT^.pServerBaseAddress := pMMT^.pClientBaseAddress; - pMMT^.pClientBaseAddress := pMMT; - // translate to the server space map - ipcFixupAddresses(True, pMMT); - // store the address map offset so the caller can retranslate - pMMT^.pServerBaseAddress := pMMT; - // return some options to the client - if DBGetContactSettingByte(0,SHLExt_Name,SHLExt_ShowNoIcons,0) <> 0 then - begin - pMMT^.dwFlags := HIPC_NOICONS; - end; - // see if we have a custom string for 'Miranda' - szMiranda := Translate('Miranda'); - lstrcpyn(pMMT^.MirandaName,szMiranda,sizeof(pMMT^.MirandaName)-1);
-
- // for the MRU menu
- szBuf := Translate('Recently');
- lstrcpyn(pMMT^.MRUMenuName, szBuf, sizeof(pMMT^.MRUMenuName)-1);
-
- // and a custom string for "clear entries"
- szBuf := Translate('Clear entries');
- lstrcpyn(pMMT^.ClearEntries,szBuf, sizeof(pMMT^.ClearEntries)-1);
- - // if the group mode is on, check if they want the CList setting - bGroupMode := BST_CHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseGroups, BST_UNCHECKED); - if bGroupMode and (BST_CHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_UseCListSetting, BST_UNCHECKED)) then - begin - bGroupMode := 1 = DBGetContactSettingByte(0, 'CList', 'UseGroups', 0); - end; - iSlot := 0; - // return profile if set - if BST_UNCHECKED = DBGetContactSettingByte(0, SHLExt_Name, SHLExt_ShowNoProfile, BST_UNCHECKED) then - begin - pct := ipcAlloc(pMMT, 50); - if pct <> nil then - begin - // will actually return with .dat if there's space for it, not what the docs say - pct^.Status := STATUS_PROFILENAME; - PluginLink^.CallService(MS_DB_GETPROFILENAME, 49, Integer(pct)+sizeof(TSlotIPC)); - end; //if - end; //if - if (bits^ and REQUEST_NEWICONS) = REQUEST_NEWICONS then - begin - ipcGetSkinIcons(pMMT); - end; - if (bits^ and REQUEST_GROUPS = REQUEST_GROUPS) then - begin - // return contact's grouping if it's present - while bGroupMode do - begin - Str(iSlot, szGroupStr); - if DBGetContactSetting(0, 'CListGroups', szGroupStr, @dbv) <> 0 then Break; - pct := ipcAlloc(pMMT, lstrlen(dbv.pszVal+1)+1); // first byte has flags, need null term - if pct <> nil then - begin - if pMMT^.GroupsBegin = nil then pMMT^.GroupsBegin := pct; - pct^.fType := REQUEST_GROUPS; - pct^.hContact := 0; - Integer (szBuf) := Integer(pct) + sizeof(TSlotIPC); // get the end of the slot - strcpy(szBuf, dbv.pszVal+1); - pct^.hGroup := 0; - DBFreeVariant(@dbv); // free the string - end else begin - // outta space - DBFreeVariant(@dbv); - Break; - end; //if - Inc(iSlot); - end; {while} - // if there was no space left, it'll end on null - if pct = nil then bits^ := (bits^ or GROUPS_NOTIMPL) and not REQUEST_GROUPS; - end; {if: group request} - // SHOULD check slot space. - if (bits^ and REQUEST_CONTACTS = REQUEST_CONTACTS) then - begin - if not ipcGetSortedContacts(pMMT, @iSlot, bGroupMode) then - begin - // fail if there were no contacts AT ALL - bits^ := (bits^ or CONTACTS_NOTIMPL) and not REQUEST_CONTACTS; - end; //if - end; // if:contact request - // store the number of slots allocated - pMMT^.Slots := iSlot; - Reply: - { get the handle the caller wants to be signalled on } - hSignal := OpenEvent(EVENT_ALL_ACCESS, False, pMMT^.SignalEventName); - { did it open? } - If hSignal <> 0 then - begin - { signal and close } - SetEvent(hSignal); CloseHandle(hSignal); - end; - { unmap the shared memory from this process } - UnmapViewOfFile(pMMT); - end; - { close the map file } - CloseHandle(hMap); - end; {if} - // - end; - - function ThreadServer(hMainThread: Pointer): Cardinal; - {$ifdef FPC} - stdcall; - {$endif} - var - hEvent: THandle; - begin - CallService(MS_SYSTEM_THREAD_PUSH,0,0); - hEvent := CreateEvent(nil, False, False, PChar(CreateProcessUID(GetCurrentProcessId()))); - while True do - begin - Result := WaitForSingleObjectEx(hEvent,INFINITE,True); - if Result = WAIT_OBJECT_0 then - begin - QueueUserAPC(@ipcService,THandle(hMainThread),0); - end; //if - if CallService(MS_SYSTEM_TERMINATED,0,0)=1 then Break; - end; //while - CloseHandle(hEvent); - CloseHandle(THandle(hMainThread)); - CallService(MS_SYSTEM_THREAD_POP,0,0); - ExitThread(0); - end; - - procedure InvokeThreadServer; - var - {$ifdef FPC} - TID: LongWord; - {$else} - TID: Cardinal; - {$endif} - var - hMainThread: THandle; - begin - hMainThread := 0; - DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),@hMainThread,THREAD_SET_CONTEXT,FALSE,0); - if hMainThread <> 0 then - begin - {$ifdef FPC} - CloseHandle(CreateThread(nil,0,@ThreadServer,Pointer(hMainThread),0,TID)); - {$else} - CloseHandle(BeginThread(nil,0,@ThreadServer,Pointer(hMainThread),0,TID)); - {$endif} - end; //if - end; - -{ exported functions } - - function DllGetClassObject(const CLSID: TCLSID; const IID: TIID; var Obj): HResult; stdcall; - begin - Pointer(Obj) := nil; - Result := CLASS_E_CLASSNOTAVAILABLE; - if (IsEqualCLSID(CLSID,CLSID_ISHLCOM)) - and (IsEqualIID(IID,IID_IClassFactory)) - and (FindWindow(MIRANDANAME,nil) <> 0) then - begin - Pointer(Obj) := TClassFactoryRec_Create; - Result := S_OK; - end; //if - end; - - function DllCanUnloadNow: HResult; - begin - if ((dllpublic.FactoryCount = 0) and (dllpublic.ObjectCount = 0)) then - begin - Result := S_OK; - end else begin - Result := S_FALSE; - end; //if - end; - -{ helper functions } - -type - - PSHELLEXECUTEINFO = ^TSHELLEXECUTEINFO; - TSHELLEXECUTEINFO = record - cbSize: DWORD; - fMask: LongInt; - hwnd: THandle; - lpVerb: PChar; - lpFile: PChar; - lpParameters: PChar; - lpDirectory: PChar; - nShow: Integer; - hInstApp: THandle; - lpIDLIst: Pointer; - lpClass: PChar; - hKey: THandle; - dwHotKey: DWORD; - hIcon: THandle; // is union - hProcess: THandle; - end; - - function ShellExecuteEx(var se: TSHELLEXECUTEINFO): Boolean; stdcall; external 'shell32.dll' name 'ShellExecuteExA'; - - function wsprintfs(lpOut, lpFmt: PChar; ArgS: PChar): Integer; cdecl; external 'user32.dll' name 'wsprintfA'; - - function RemoveCOMRegistryEntries: HResult; - var - hRootKey: HKEY; - begin - if RegOpenKeyEx(HKEY_CLASSES_ROOT, 'miranda.shlext', 0, KEY_READ, hRootKey) = ERROR_SUCCESS then - begin - (* need to delete the subkey before the parent key is deleted under NT/2000/XP *) - RegDeleteKey(hRootKey, 'CLSID'); - (* close the key *) - RegCloseKey(hRootKey); - (* delete it *) - if RegDeleteKey(HKEY_CLASSES_ROOT, 'miranda.shlext') <> ERROR_SUCCESS then - begin - MessageBox(0, 'Unable to delete registry key for "shlext COM", this key may already be deleted or you may need admin rights.', 'Problem', MB_ICONERROR); - end; //if - end; //if - if RegOpenKeyEx(HKEY_CLASSES_ROOT, '\*\shellex\ContextMenuHandlers', 0, KEY_ALL_ACCESS, hRootKey) = ERROR_SUCCESS then - begin - if RegDeleteKey(hRootKey, 'miranda.shlext') <> ERROR_SUCCESS then - begin - MessageBox(0, 'Unable to delete registry key for "File context menu handlers", this key may already be deleted or you may need admin rights.', 'Problem', MB_ICONERROR); - end; //if - RegCloseKey(hRootKey); - end; //if - if RegOpenKeyEx(HKEY_CLASSES_ROOT, 'Directory\shellex\ContextMenuHandlers', 0, KEY_ALL_ACCESS, hRootKey) = ERROR_SUCCESS then - begin - if RegDeleteKey(hRootKey, 'miranda.shlext') <> ERROR_SUCCESS then - begin - MessageBox(0, 'Unable to delete registry key for "Directory context menu handlers", this key may already be deleted or you may need admin rights.', 'Problem', MB_ICONERROR); - end; //if - RegCloseKey(hRootKey); - end; //if - if ERROR_SUCCESS = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', 0, KEY_ALL_ACCESS, hRootKey) then - begin - if RegDeleteValue(hRootKey, '{72013A26-A94C-11d6-8540-A5E62932711D}') <> ERROR_SUCCESS then - begin - MessageBox(0, 'Unable to delete registry entry for "Approved context menu handlers", this key may already be deleted or you may need admin rights.', 'Problem', MB_ICONERROR); - end; //if - RegCloseKey(hRootKey); - end; //if - Result := S_OK; - end; - - { called by the options code to remove COM entries, and before that, get permission, if required. - } - - procedure CheckUnregisterServer; - var - sei: TSHELLEXECUTEINFO; - szBuf: array[0..MAX_PATH*2] of Char; - szFileName: array[0..MAX_PATH] of Char; - begin - if not VistaOrLater then - begin - RemoveCOMRegistryEntries(); - Exit; - end; - // launches regsvr to remove the dll under admin. - GetModuleFileName(System.hInstance, szFileName, sizeof(szFileName)); - wsprintfs(szBuf, '/s /u "%s"', szFileName); - ZeroMemory(@sei, sizeof(sei)); - sei.cbSize := sizeof(sei); - sei.lpVerb := 'runas'; - sei.lpFile := 'regsvr32'; - sei.lpParameters := szBuf; - ShellExecuteEx(sei); - Sleep(1000); - RemoveCOMRegistryEntries(); - end; - - { Wow, I can't believe there isn't a direct API for this - 'runas' will invoke the UAC and ask - for permission before installing the shell extension. note the filepath arg has to be quoted } - procedure CheckRegisterServer; - var - hRegKey: HKEY; - sei: TSHELLEXECUTEINFO; - szBuf: array[0..MAX_PATH*2] of Char; - szFileName: array[0..MAX_PATH] of Char; - begin - if ERROR_SUCCESS = RegOpenKeyEx(HKEY_CLASSES_ROOT, 'miranda.shlext', 0, KEY_READ, hRegKey) then - begin - RegCloseKey(hRegKey); - end else begin - if VistaOrLater then - begin - MessageBox(0, 'Shell context menus requires your permission to register with Windows Explorer (one time only).', - 'Miranda IM - Shell context menus (shlext.dll)', MB_OK or MB_ICONINFORMATION); - // /s = silent - GetModuleFileName(System.hInstance, szFileName, sizeof(szFileName)); - wsprintfs(szBuf, '/s "%s"', szFileName); - ZeroMemory(@sei, sizeof(sei)); - sei.cbSize := sizeof(sei); - sei.lpVerb := 'runas'; - sei.lpFile := 'regsvr32'; - sei.lpParameters := szBuf; - ShellExecuteEx(sei); - end; - end; - end; - -initialization -begin - FillChar(dllpublic, sizeof(dllpublic), 0); - IsMultiThread := True; - VistaOrLater := GetProcAddress(GetModuleHandle('kernel32'), 'GetProductInfo') <> nil; -end; - + { close the map file }
+ CloseHandle(hMap);
+ end; { if }
+ //
+end;
+
+function ThreadServer(hMainThread: Pointer): Cardinal;
+{$IFDEF FPC}
+stdcall;
+{$ENDIF}
+var
+ hEvent: THandle;
+begin
+ Thread_Push(0,0);
+ hEvent := CreateEvent(nil, False, False, PChar(CreateProcessUID(GetCurrentProcessId())));
+ while True do
+ begin
+ Result := WaitForSingleObjectEx(hEvent, INFINITE, True);
+ if Result = WAIT_OBJECT_0 then
+ begin
+ QueueUserAPC(@ipcService, THandle(hMainThread), 0);
+ end; // if
+ if CallService(MS_SYSTEM_TERMINATED, 0, 0) = 1 then
+ break;
+ end; // while
+ CloseHandle(hEvent);
+ CloseHandle(THandle(hMainThread));
+ Thread_Pop();
+ ExitThread(0);
+end;
+
+procedure InvokeThreadServer;
+var
+{$IFDEF FPC}
+ tid: LongWord;
+{$ELSE}
+ tid: Cardinal;
+{$ENDIF}
+var
+ hMainThread: THandle;
+begin
+ hMainThread := 0;
+ DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), @hMainThread,
+ THREAD_SET_CONTEXT, False, 0);
+ if hMainThread <> 0 then
+ begin
+{$IFDEF FPC}
+ CloseHandle(CreateThread(nil, 0, @ThreadServer, Pointer(hMainThread), 0, tid));
+{$ELSE}
+ CloseHandle(BeginThread(nil, 0, @ThreadServer, Pointer(hMainThread), 0, tid));
+{$ENDIF}
+ end; // if
+end;
+
+{ exported functions }
+
+function DllGetClassObject(const CLSID: TCLSID; const IID: TIID; var Obj): HResult; stdcall;
+begin
+ Pointer(Obj) := nil;
+ Result := CLASS_E_CLASSNOTAVAILABLE;
+ if (IsEqualCLSID(CLSID, CLSID_ISHLCOM)) and (IsEqualIID(IID, IID_IClassFactory)) and
+ (FindWindow(MirandaName, nil) <> 0) then
+ begin
+ Pointer(Obj) := TClassFactoryRec_Create;
+ Result := S_OK;
+ end; // if
+end;
+
+function DllCanUnloadNow: HResult;
+begin
+ if ((dllpublic.FactoryCount = 0) and (dllpublic.ObjectCount = 0)) then
+ begin
+ Result := S_OK;
+ end
+ else
+ begin
+ Result := S_FALSE;
+ end; // if
+end;
+
+{ helper functions }
+
+type
+
+ PSHELLEXECUTEINFO = ^TSHELLEXECUTEINFO;
+
+ TSHELLEXECUTEINFO = record
+ cbSize: DWORD;
+ fMask: LongInt;
+ hwnd: THandle;
+ lpVerb: PChar;
+ lpFile: PChar;
+ lpParameters: PChar;
+ lpDirectory: PChar;
+ nShow: Integer;
+ hInstApp: THandle;
+ lpIDLIst: Pointer;
+ lpClass: PChar;
+ HKEY: THandle;
+ dwHotkey: DWORD;
+ HICON: THandle; // is union
+ hProcess: THandle;
+ end;
+
+function ShellExecuteEx(var se: TSHELLEXECUTEINFO): Boolean; stdcall;
+ external 'shell32.dll' name 'ShellExecuteExA';
+
+function wsprintfs(lpOut, lpFmt: PChar; args: PChar): Integer; cdecl;
+ external 'user32.dll' name 'wsprintfA';
+
+function RemoveCOMRegistryEntries: HResult;
+var
+ hRootKey: HKEY;
+begin
+ if RegOpenKeyEx(HKEY_CLASSES_ROOT, 'miranda.shlext', 0, KEY_READ, hRootKey) = ERROR_SUCCESS
+ then
+ begin
+ (* need to delete the subkey before the parent key is deleted under NT/2000/XP *)
+ RegDeleteKey(hRootKey, 'CLSID');
+ (* close the key *)
+ RegCloseKey(hRootKey);
+ (* delete it *)
+ if RegDeleteKey(HKEY_CLASSES_ROOT, 'miranda.shlext') <> ERROR_SUCCESS then
+ begin
+ MessageBox(0,
+ 'Unable to delete registry key for "shlext COM", this key may already be deleted or you may need admin rights.',
+ 'Problem', MB_ICONERROR);
+ end; // if
+ end; // if
+ if RegOpenKeyEx(HKEY_CLASSES_ROOT, '\*\shellex\ContextMenuHandlers', 0, KEY_ALL_ACCESS,
+ hRootKey) = ERROR_SUCCESS then
+ begin
+ if RegDeleteKey(hRootKey, 'miranda.shlext') <> ERROR_SUCCESS then
+ begin
+ MessageBox(0,
+ 'Unable to delete registry key for "File context menu handlers", this key may already be deleted or you may need admin rights.',
+ 'Problem', MB_ICONERROR);
+ end; // if
+ RegCloseKey(hRootKey);
+ end; // if
+ if RegOpenKeyEx(HKEY_CLASSES_ROOT, 'Directory\shellex\ContextMenuHandlers', 0, KEY_ALL_ACCESS,
+ hRootKey) = ERROR_SUCCESS then
+ begin
+ if RegDeleteKey(hRootKey, 'miranda.shlext') <> ERROR_SUCCESS then
+ begin
+ MessageBox(0,
+ 'Unable to delete registry key for "Directory context menu handlers", this key may already be deleted or you may need admin rights.',
+ 'Problem', MB_ICONERROR);
+ end; // if
+ RegCloseKey(hRootKey);
+ end; // if
+ if ERROR_SUCCESS = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ 'Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved', 0, KEY_ALL_ACCESS,
+ hRootKey) then
+ begin
+ if RegDeleteValue(hRootKey, '{72013A26-A94C-11d6-8540-A5E62932711D}') <> ERROR_SUCCESS then
+ begin
+ MessageBox(0,
+ 'Unable to delete registry entry for "Approved context menu handlers", this key may already be deleted or you may need admin rights.',
+ 'Problem', MB_ICONERROR);
+ end; // if
+ RegCloseKey(hRootKey);
+ end; // if
+ Result := S_OK;
+end;
+
+{ called by the options code to remove COM entries, and before that, get permission, if required.
+}
+
+procedure CheckUnregisterServer;
+var
+ sei: TSHELLEXECUTEINFO;
+ szBuf: array [0 .. MAX_PATH * 2] of Char;
+ szFileName: array [0 .. MAX_PATH] of Char;
+begin
+ if not VistaOrLater then
+ begin
+ RemoveCOMRegistryEntries();
+ Exit;
+ end;
+ // launches regsvr to remove the dll under admin.
+ GetModuleFileName(System.hInstance, szFileName, sizeof(szFileName));
+ wsprintfs(szBuf, '/s /u "%s"', szFileName);
+ ZeroMemory(@sei, sizeof(sei));
+ sei.cbSize := sizeof(sei);
+ sei.lpVerb := 'runas';
+ sei.lpFile := 'regsvr32';
+ sei.lpParameters := szBuf;
+ ShellExecuteEx(sei);
+ Sleep(1000);
+ RemoveCOMRegistryEntries();
+end;
+
+{ Wow, I can't believe there isn't a direct API for this - 'runas' will invoke the UAC and ask
+ for permission before installing the shell extension. note the filepath arg has to be quoted }
+procedure CheckRegisterServer;
+var
+ hRegKey: HKEY;
+ sei: TSHELLEXECUTEINFO;
+ szBuf: array [0 .. MAX_PATH * 2] of Char;
+ szFileName: array [0 .. MAX_PATH] of Char;
+begin
+ if ERROR_SUCCESS = RegOpenKeyEx(HKEY_CLASSES_ROOT, 'miranda.shlext', 0, KEY_READ, hRegKey)
+ then
+ begin
+ RegCloseKey(hRegKey);
+ end
+ else
+ begin
+ if VistaOrLater then
+ begin
+ MessageBox(0,
+ 'Shell context menus requires your permission to register with Windows Explorer (one time only).',
+ 'Miranda IM - Shell context menus (shlext.dll)', MB_OK or MB_ICONINFORMATION);
+ // /s = silent
+ GetModuleFileName(System.hInstance, szFileName, sizeof(szFileName));
+ wsprintfs(szBuf, '/s "%s"', szFileName);
+ ZeroMemory(@sei, sizeof(sei));
+ sei.cbSize := sizeof(sei);
+ sei.lpVerb := 'runas';
+ sei.lpFile := 'regsvr32';
+ sei.lpParameters := szBuf;
+ ShellExecuteEx(sei);
+ end;
+ end;
+end;
+
+initialization
+
+begin
+ FillChar(dllpublic, sizeof(dllpublic), 0);
+ IsMultiThread := True;
+ VistaOrLater := GetProcAddress(GetModuleHandle('kernel32'), 'GetProductInfo') <> nil;
+end;
+
end. - |