;################################################################

format PE GUI 4.0 DLL
entry DllMain

;################################################################

include '%fasminc%\win32ax.inc'
include '%fasminc%\api\kernel32.inc'
include '%fasminc%\api\user32.inc'
include '%fasminc%\api\shell32.inc'
include '%fasminc%\api\advapi32.inc'

;################################################################

S_OK				equ 0
CP_ACP				equ 0
MAX_PATH			equ 260
REGDB_E_INVALIDVALUE		equ 0x80040153
ERROR_NO_MORE_ITEMS		equ 259
CLASS_E_CLASSNOTAVAILABLE	equ 0x80040111
CLASS_E_NOAGGREGATION		equ 0x80040110
E_OUTOFMEMORY			equ 0x8007000E
E_NOINTERFACE			equ 0x80004002
E_INVALIDARG			equ 0x80070057
DVASPECT_CONTENT		equ 1
CMF_DEFAULTONLY			equ 00000001h
GCS_HELPTEXTA			equ 1
GCS_HELPTEXTW			equ 5
TYMED_HGLOBAL			equ 1
SEVERITY_SUCCESS		equ 0
FACILITY_NULL			equ 0

;################################################################

section '.data' data readable writeable

;----------------------------------------------------------------;
; macros                                                         ;
;----------------------------------------------------------------;
macro		MAKE_HR severity,facility,code
{
		mov	ecx,[severity]
		shl	ecx,31
		mov	eax,[facility]
		shl	eax,16
		or	eax,ecx
		or	eax,[code]
}

;----------------------------------------------------------------;
; declare GUID structure                                         ;
;----------------------------------------------------------------;
struct			GUID
Data1			dd ?
Data2			dw ?
Data3			dw ?
Data4			db 8 dup (?)
ends

;----------------------------------------------------------------;
; interfaces                                                     ;
;----------------------------------------------------------------;
interface	IClassFactory,\
		QueryInterface,\
		AddRef,\
		Release,\
		CreateInstance,\
		LockServer

interface	IShellExtInit,\
		QueryInterface,\
		AddRef,\
		Release,\
		Initialize

;interface	IContextMenu,\
;		QueryInterface,\
;		AddRef,\
;		Release,\
;		QueryContextMenu,\
;		InvokeCommand,\
;		GetCommandString

interface	IDataObject,\
		QueryInterface,\
		AddRef,\
		Release,\
		GetData,\
		RemoteGetData,\
		GetDataHere,\
		RemoteGetDataHere,\
		QueryGetData,\
		GetCanonicalFormatEtc,\
		SetData,\
		RemoteSetData,\
		EnumFormatEtc,\
		DAdvise,\
		DUnadvise,\
		EnumDAdvise

;----------------------------------------------------------------;
; define the CLSID and IID                                       ;
;----------------------------------------------------------------;
CLSID_ShellExt		GUID <0x02a325c2>,<0x648f>,<0x11d5>,<0xa6,0x5a,0,0xe0,0x29,7,0x4d,0x77>
IID_IUnknown		GUID <0x00000000>,<0>,<0>,<0xc0,0,0,0,0,0,0,0x46>
IID_IClassFactory	GUID <0x00000001>,<0>,<0>,<0xc0,0,0,0,0,0,0,0x46>
IID_IContextMenu	GUID <0x000214e4>,<0>,<0>,<0xc0,0,0,0,0,0,0,0x46>
IID_IShellExtInit	GUID <0x000214e8>,<0>,<0>,<0xc0,0,0,0,0,0,0,0x46>

;----------------------------------------------------------------;
; declare the IClassFactory object structure (static)            ;
;----------------------------------------------------------------;
struct			oCF
lpVtblIClassFactory	dd ?						; IClassFactory vtable
ICFRefCount		dd ?						; reference count
ends

;----------------------------------------------------------------;
; declare the IShellExtInit and ContextMenu objects structure    ;
;----------------------------------------------------------------;
struct			oSE
lpVtblShellExtInit	dd ?						; IShellExtInit vtable
lpVtblContextMenu	dd ?						; IContextMenu vtable
m_nRefCount		dd ?						; object reference count
m_hMenuString		dd ?
m_szFile		db MAX_PATH dup (?)
ends

;----------------------------------------------------------------;
; declare vtables                                                ;
;----------------------------------------------------------------;
IClassFactoryVT		dd QueryInterface_CF
			dd AddRef_CF
			dd Release_CF
			dd CreateInstance
			dd LockServer

IShellExtInitVT		dd QueryInterface
			dd AddRef
			dd Release
			dd Initialize

IContextMenuVT		dd QueryInterface
			dd AddRef
			dd Release
			dd QueryContextMenu
			dd InvokeCommand
			dd GetCommandString

;----------------------------------------------------------------;
; declare miscellaneous structures                               ;
;----------------------------------------------------------------;
struct			FORMATETC
cfFormat		dw ?
ptd			dd ?
dwAspect		dd ?
lindex			dd ?
tymed			dd ?
ends

struct			STGMEDIUM
tymed			dd ?
union
	hBitmap		dd ?
	hMetaFilePict	dd ?
	hEnhMetaFile	dd ?
	hGlobal		dd ?
	lpszFileName	dd ?
	pstm		dd ?
	pstg		dd ?
ends
pUnkForRelease		dd ?
ends

struct			MENUITEMINFOA
cbSize			dd ?
fMask			dd ?
fType			dd ?
fState			dd ?
wID			dd ?
hSubMenu		dd ?
hbmpChecked		dd ?
hbmpUnchecked		dd ?
dwItemData		dd ?
dwTypeData		dd ?
cch			dd ?
ends

struct			CMINVOKECOMMANDINFO
cbSize			dd ?
fMask			dd ?
hwnd			dd ?
lpVerb			dd ?
lpParameters		dd ?
lpDirectory		dd ?
nShow			dd ?
dwHotKey		dd ?
hIcon			dd ?
ends

;----------------------------------------------------------------;
; other data                                                     ;
;----------------------------------------------------------------;
szSampleDesc		db 'Sample shell extention server',0
szInprocServer32	db 'InProcServer32',0
szServerRegPath		db '*\shellex\ContextMenuHandlers\CustomContext',0
szThreadModel		db 'ThreadingModel',0
szThreadType		db 'Apartment',0
szCLSID			db 'CLSID',0
szMenuString		db 'Test Menu',0
szContextMenuHandler	db 'Context Menu Handler',0
szHelpText		db 'CustomContext help text',0

g_cRefThisDll		dd ?						; DLL reference count
g_hmodThisDll		dd ?						; DLL hInstance
pszFilePath 		dd ?
BufLen			dd ?

pICF			oCF IClassFactoryVT,0

;################################################################

section '.code' code readable executable
proc		DllMain hinstDLL,fdwReason,lpvReserved
		.if	[fdwReason] = DLL_PROCESS_ATTACH
			mov	eax,[hinstDLL]
			mov	[g_hmodThisDll],eax
			mov	eax,TRUE
		.elseif
			mov	eax,FALSE
		.endif
		ret
endp

;================================================================

proc		DllRegisterServer
local		hKey:DWORD,\
		hKey2:DWORD,\
		hKey3:DWORD,\
		sBuf[MAX_PATH]:BYTE,\
		wsBuf[MAX_PATH]:WORD,\
		psBuf:DWORD,\
		pwsBuf:DWORD,\
		pti:DWORD

; assign the pointers to the local text buffers
		lea	eax,[sBuf]
		mov	[psBuf],eax
		lea	eax,[wsBuf]
		mov	[pwsBuf],eax

; convert GUID
		invoke	StringFromGUID2,CLSID_ShellExt,[pwsBuf],MAX_PATH
		invoke	WideCharToMultiByte,CP_ACP,0,[pwsBuf],-1,[psBuf],MAX_PATH,0,0

; create HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\%ServerName%
		lea	eax,[hKey]
		invoke	RegCreateKey,HKEY_CLASSES_ROOT,szServerRegPath,eax
		.if	eax <> 0
			jmp	@f
		.endif

; set Default value as GUID
		invoke	lstrlen,[psBuf]
		invoke	RegSetValue,[hKey],0,REG_SZ,[psBuf],eax
		.if	eax <> 0
			jmp	@f
		.endif

; close key
		invoke	RegCloseKey,[hKey]

; open HKEY_CLASSES_ROOT\CLSID
		lea	eax,[hKey]
		invoke	RegOpenKey,HKEY_CLASSES_ROOT,szCLSID,eax
		.if	eax <> 0
			jmp	@f
		.endif

; create HKEY_CLASSES_ROOT\CLSID\%GUID%
		lea	eax,[hKey2]
		invoke	RegCreateKey,[hKey],[psBuf],eax
		.if	eax <> 0
			jmp	@f
		.endif

; set Default value as GUID
		invoke	lstrlen,[psBuf]
		invoke	RegSetValue,[hKey2],0,REG_SZ,[psBuf],eax
		.if	eax <> 0
			jmp	@f
		.endif

; create HKEY_CLASSES_ROOT\CLSID\%GUID%\InprocServer32
		lea	eax,[hKey3]
		invoke	RegCreateKey,[hKey2],szInprocServer32,eax
		.if	eax <> 0
			jmp	@f
		.endif

; get module file path
		invoke	GetModuleFileName,[g_hmodThisDll],[psBuf],MAX_PATH
		.if	eax = 0
			mov	eax,1
			jmp	@f
		.endif

; set Default value as module file path
		invoke	RegSetValue,[hKey3],0,REG_SZ,[psBuf],MAX_PATH
		.if	eax <> 0
			jmp	@f
		.endif

; set the threading type value
		invoke	lstrlen,szThreadType
		invoke	RegSetValueEx,[hKey3],szThreadModel,0,REG_SZ,szThreadType,eax
		.if	eax <> 0
			jmp	@f
		.endif

; close keys
		invoke	RegCloseKey,[hKey3]
		.if	[hKey] <> 0
			invoke	RegCloseKey,[hKey]
		.endif
		.if	[hKey2] <> 0
			invoke	RegCloseKey,[hKey2]
		.endif
		.if	[hKey3] <> 0
			invoke	RegCloseKey,[hKey3]
		.endif
		mov	eax,S_OK
@@:		ret
endp

;================================================================

proc		DllUnregisterServer
local		hSubkey:DWORD,\
		hSubkey2:DWORD,\
		sBuf[MAX_PATH]:BYTE,\
		psBuf:DWORD,\
		wsBuf[MAX_PATH]:WORD,\
		pwsBuf:DWORD

; assign the pointers to the local text buffers
		lea	eax,[sBuf]
		mov	[psBuf],eax
		lea	eax,[wsBuf]
		mov	[pwsBuf],eax

; convert GUID
		invoke	StringFromGUID2,CLSID_ShellExt,[pwsBuf],MAX_PATH
		invoke	WideCharToMultiByte,CP_ACP,0,[pwsBuf],-1,[psBuf],MAX_PATH,0,0

; recursively delete *\shellex\ContextMenuHandlers\CustomContext
		stdcall	GuardedDeleteKey,HKEY_CLASSES_ROOT,szServerRegPath
		.if	eax <> 0
			jmp	@f
		.endif

; open HKEY_CLASSES_ROOT\CLSID
		lea	eax,[hSubkey]
		invoke	RegOpenKey,HKEY_CLASSES_ROOT,szCLSID,eax

; recursively delete HKEY_CLASSES_ROOT\CLSID\%GUID%
		stdcall	GuardedDeleteKey,[hSubkey],[psBuf]
		.if	eax <> 0
			jmp	@f
		.endif

; close keys
		invoke	RegCloseKey,[hSubkey]
		mov	eax,S_OK
@@:		ret
endp

;================================================================

proc		GuardedDeleteKey hKey,lpszSubKey
local		szSubKeyName[MAX_PATH+1]:BYTE,\
		hSubkey:DWORD

; open \key\subkey
		lea	eax,[hSubkey]
		invoke	RegOpenKey,[hKey],[lpszSubKey],eax
		.if	eax <> 0
			mov	eax,REGDB_E_INVALIDVALUE
			jmp	@f
		.endif

KillNextSubkey:	lea	eax,[szSubKeyName]				; check for a subkey
		invoke	RegEnumKey,[hSubkey],0,eax,MAX_PATH+1
		.if	eax <> ERROR_NO_MORE_ITEMS			; subkeys exist, delete the subkey throught recusion
			lea	eax,[szSubKeyName]
			stdcall	GuardedDeleteKey,[hSubkey],eax
			jmp	KillNextSubkey
		.else
		.endif
		invoke	RegCloseKey,[hSubkey]

; no more subkeys, delete the specfied key
		invoke	RegDeleteKey,[hKey],[lpszSubKey]
		.if	eax = 0
			mov	eax,S_OK
		.endif
@@:		ret
endp

;================================================================

proc		DllCanUnloadNow
		mov	ecx,[g_cRefThisDll]
		xor	eax,eax
		test	ecx,ecx						; if global reference count = 0
		setne	al						; set eax to 0
		ret
endp

;================================================================

proc		DllGetClassObject pCLSID,pIID,ppv
local		hr:DWORD

		invoke	IsEqualGUID,[pCLSID],CLSID_ShellExt
		.if	eax = TRUE
			inc	[g_cRefThisDll]				; increase global reference count

			mov	eax,pICF				; get requested pointer (QueryInterface,pIID,ppv)
			push	[ppv]
			push	[pIID]
			push	eax
			mov	eax,[eax]
			call	[eax + IClassFactory.QueryInterface]
			mov	[hr],eax				; return result of QueryInterface
		.else
			mov	[hr],CLASS_E_CLASSNOTAVAILABLE
		.endif
		mov	eax,[hr]
		ret
endp

;================================================================

; QueryInterface for the ClassFactory Interface
proc		QueryInterface_CF this,pRIID,ppv
local		TEMP:DWORD

		invoke	IsEqualGUID,[pRIID],IID_IUnknown
		mov	[TEMP],eax
		invoke	IsEqualGUID,[pRIID],IID_IClassFactory
		or	eax,[TEMP]
			.if	eax = TRUE				; asking for IUnknown or IClassFactory
				mov	eax,[this]
				mov	edx,[ppv]
				mov	[edx],eax			; so we point to ourselves

				push	eax
				mov	eax,[eax]
				call	[eax + IClassFactory.AddRef]	; increase reference count for new pointer

				mov	eax,S_OK			; signal all is well
				jmp	@f
			.endif
		mov	[ppv],0						; set pointer as NULL (bad)
		mov	eax,E_NOINTERFACE				; signal interface not supported
@@:		ret
endp

;================================================================

proc		AddRef_CF this
		inc	[pICF.ICFRefCount]
		mov	eax,[pICF.ICFRefCount]
		ret
endp

;================================================================

proc		Release_CF this
		dec	[pICF.ICFRefCount]
		mov	eax,[pICF.ICFRefCount]
		.if	eax = 0
			dec	[g_cRefThisDll]
		.endif
		ret
endp

;================================================================

proc		CreateInstance this,pUnknownOuter,iid,ppv
local		hr:DWORD

		.if	[pUnknownOuter] <> 0
			mov	eax,CLASS_E_NOAGGREGATION		; no support for aggregation
		.else
			invoke	CoTaskMemAlloc,sizeof.oSE		; create new ShellExt object
			.if	eax <> 0
				mov	[eax + oSE.lpVtblShellExtInit],IShellExtInitVT	; initialize new object
				mov	[eax + oSE.lpVtblContextMenu],IContextMenuVT
				mov	[eax + oSE.m_nRefCount],0
				mov	[eax + oSE.m_hMenuString],szMenuString
				inc	[g_cRefThisDll]
			.endif
			.if	eax = 0					; create failed
				mov	eax,E_OUTOFMEMORY
			.else
				inc	[pICF.ICFRefCount]		; increase reference count of new object

				push	[ppv]				; query new object for client-requested interface
				push	[iid]
				push	eax
				mov	eax,[eax]
				call	[eax + IShellExtInit.QueryInterface]
				mov	[hr],eax
			.endif
		.endif
		mov	eax,[hr]
		ret
endp

;================================================================

proc		LockServer bLockServer:DWORD
		xor	eax,eax
		ret
endp

;================================================================

; QueryInterface for the IShellExtInit and IContextMenu interfaces
proc		QueryInterface this,pRIID,ppv
local		TEMP:DWORD,\
		ppvt:DWORD

		invoke	IsEqualGUID,[pRIID],IID_IShellExtInit		; check for supported interfaces
		mov	[TEMP],eax
		invoke	IsEqualGUID,[pRIID],IID_IUnknown
		or	eax,[TEMP]
		.if	eax = TRUE					; asking for IUnknown or IShellExtInit
			mov	eax,[this]
			mov	edx,[ppv]
			mov	[edx],eax				; so we point to ourselves
			mov	[ppvt],eax				; *ppv = (LPSHELLEXTINIT)this;

			mov	eax,[ppvt]				; increase reference count for new pointer
			push	eax
			mov	eax,[eax]
			call	[eax + IShellExtInit.AddRef]

			mov	eax,S_OK				; signal all is well
			jmp	@f
		.endif

		invoke	IsEqualGUID,[pRIID],IID_IContextMenu
		.if	eax = TRUE					; asking for IContextMenu
			mov	eax,[this]				; increase reference count for new pointer
			push	eax
			mov	eax,[eax]
			call	[eax + IShellExtInit.AddRef]

			mov	edx,[ppv]
			mov	eax,[this]
			add	eax,4					; ???? interface cast
			mov	[edx],eax
			mov	[ppvt],eax				; *ppv = (LPCONTEXTMENU)this;
			mov	eax,S_OK				; signal all is well
			jmp	@f
		.endif

		xor	eax,eax						; return NULL (bad) pointer
		mov	edx,[ppv]
		mov	[edx],eax
		mov	eax,E_NOINTERFACE				; signal interface is not supported
@@:		ret
endp

;================================================================

proc		AddRef this
		mov	eax,[this]
		inc	[eax + oSE.m_nRefCount]
		mov	eax,[eax + oSE.m_nRefCount]
		ret							; return object count
endp

;================================================================

proc		Release this
		mov	eax,[this]
		dec	[eax + oSE.m_nRefCount]
		mov	eax,[eax + oSE.m_nRefCount]
		.if	eax = 0						; reference count has dropped to zero, delete it
			invoke	CoTaskMemFree,[this]
			dec	[pICF.ICFRefCount]
			xor	eax,eax					; count = 0
		.endif
		ret							; return object count
endp

;================================================================

; synopsis:	initializes shortcut menu extension
; parameters:	pidlFolder = pointer to an ITEMIDLIST structure that uniquely identifies a folder (NULL for file objects or specifies a folder for folder background shortcut menus)
;		pdtobj = pointer to an IDataObject interface object that can be used to retrieve the objects being acted upon
;		hkeyProgID = registry key for the file object or folder type (the file class)
; return value: S_OK on success, error value on failure

proc		Initialize this,pidlFolder,pdtobj,hkeyProgID
local		fmt:FORMATETC,\
		stg:STGMEDIUM,\
		HRESULT:DWORD

		mov	eax,[this]

		lea	eax,[eax + oSE.m_szFile]
		mov	[pszFilePath],eax

		mov	[fmt.cfFormat],CF_HDROP				; initialize FORMATETC structure
		mov	[fmt.ptd],0
		mov	[fmt.dwAspect],DVASPECT_CONTENT
		mov	[fmt.lindex],-1
		mov	[fmt.tymed],TYMED_HGLOBAL
		mov	[stg.tymed],TYMED_HGLOBAL			; initialize STGMEDIUM structure

		lea	eax,[stg]					; pointer to STGMEDIUM structure
		push	eax
		lea	eax,[fmt]					; pointer to FORMATETC structure
		push	eax
		mov	eax,[pdtobj]					; pointer to an IDataObject interface object
		push	eax
		mov	eax,[eax]
		call	[eax + IDataObject.GetData]			; look for CF_HDROP data in the data object

		test	eax,eax						; S_OK?
		jns	@f
		mov	[HRESULT],E_INVALIDARG
		jmp	InitEnd

@@:		cmp	[stg.hGlobal],0					; valid global handle?
		jne	@f
		mov	[HRESULT],E_INVALIDARG
		jmp	InitEnd

@@:		invoke	DragQueryFile,[stg.hGlobal],-1,0,0		; get file count
		test	eax,eax						; no files?
		jne	@f
		lea	eax,[stg]					; release medium and end on error
		invoke	ReleaseStgMedium,eax
		mov	[HRESULT],E_INVALIDARG
		jmp	InitEnd

@@:		invoke	DragQueryFile,[stg.hGlobal],0,[pszFilePath],MAX_PATH	; get filename 0
		test	eax,eax						; return value = copied characters count
		jne	@f
		mov	[HRESULT],E_INVALIDARG
		jmp	InitEnd

@@:		lea	eax,[stg]					; release medium and end on success
		invoke	ReleaseStgMedium,eax
		mov	[HRESULT],S_OK

InitEnd:	mov	eax,[HRESULT]
		ret
endp

;================================================================

; IContextMenu::QueryContextMenu
; synopsis:	adds commands to a shortcut menu (called by the shell just before the context menu is displayed)
; parameters:	hMenu		= context menu handle
;		indexMenu	= zero-based position at which to insert the first menu item
;		idCmdFirst	= lowest value for new menu ID
;		idCmdLast	= highest value for new menu ID
;		uFlags		= context of the menu event

proc		QueryContextMenu uses esi,\
		this,hMenu,indexMenu,idCmdFirst,idCmdLast,uFlags
local		HRESULT:DWORD,\
		mii:MENUITEMINFOA,\
		iCmd:DWORD

		mov	eax,[this]

		test	[uFlags],CMF_DEFAULTONLY			; user selected default command?
		je	@f
		MAKE_HR	SEVERITY_SUCCESS,FACILITY_NULL,0
		mov	[HRESULT],eax
		jmp	QueryCMEnd					; return control to the shell

@@:		lea	esi,[mii]
		mov	[esi + MENUITEMINFOA.cbSize],sizeof.MENUITEMINFOA
		mov	[esi + MENUITEMINFOA.fMask],(MIIM_ID+MIIM_STRING)
		push	[idCmdFirst]
		pop	[esi + MENUITEMINFOA.wID]
		push	[eax + oSE.m_hMenuString]
		pop	[esi + MENUITEMINFOA.dwTypeData]

		invoke	InsertMenuItem,[hMenu],[indexMenu],TRUE,esi

		MAKE_HR	SEVERITY_SUCCESS,FACILITY_NULL,1
		mov	[HRESULT],eax

QueryCMEnd:	mov	eax,[HRESULT]
		ret
endp

;================================================================

proc		GetCommandString uses esi edi,\
		this,idCmd,uFlags,Sreserved,pszName,cchMax
local		HRESULT:DWORD,\
		wcszHelpString[MAX_PATH]:WORD

		mov	eax,[idCmd]					; idCmd = offset from idCmdFirst
		.if	eax = 0						; 0 (only one menu item so far)
			mov	esi,szHelpText
			.if	[uFlags] = GCS_HELPTEXTA
				invoke	lstrcpyn,[pszName],esi,[cchMax]
			.elseif	[uFlags] = GCS_HELPTEXTW
				lea	edi,[wcszHelpString]
				invoke	WideCharToMultiByte,CP_ACP,0,esi,-1,edi,[BufLen],0,0
				mov	ecx,eax
				invoke	lstrcpynW,[pszName],ecx,[cchMax]
			.endif
		mov	[HRESULT],S_OK
		.else
			mov	[HRESULT],E_INVALIDARG
		.endif
		mov	eax,[HRESULT]
		ret
endp

;================================================================

proc		InvokeCommand uses esi,\
		this,lpcmi
local		HRESULT:DWORD

		mov	esi,[lpcmi]					; if lpVerb points to a string, ignore function
		mov	ecx,[esi + CMINVOKECOMMANDINFO.lpVerb]
		push	ecx
		mov	eax,ecx						; retrieve high word from double word argument
		shr	eax,16						; shift 16 for high word to set to high word
		.if	eax <> 0
			mov	[HRESULT],E_INVALIDARG
			jmp	InvkCmdEnd
		.endif

		pop	ecx						; get the command index
		mov	eax,ecx						; retrieve low word from double word argument
	  	and	eax,0xffff					; set to low word
		.if	eax  = 0					; 0 (only one menu item so far)
			invoke	MessageBoxEx,[esi + CMINVOKECOMMANDINFO.hwnd],pszFilePath,szContextMenuHandler,MB_OK,0
			mov	[HRESULT],S_OK
		.else
			mov	[HRESULT],E_INVALIDARG
		.endif
InvkCmdEnd:	mov	eax,[HRESULT]
		ret
endp

;================================================================

;################################################################

section '.idata' import data readable writeable
library kernel32,'KERNEL32.DLL',user32,'USER32.DLL',shell32,'SHELL32.DLL',advapi32,'ADVAPI32.DLL',ole32,'OLE32.DLL'
import ole32,\
       StringFromGUID2,'StringFromGUID2',\
       IsEqualGUID,'IsEqualGUID',\
       CoTaskMemAlloc,'CoTaskMemAlloc',\
       CoTaskMemFree,'CoTaskMemFree',\
       ReleaseStgMedium,'ReleaseStgMedium'

;################################################################

section '.edata' export data readable
export 'dll.dll',\
	DllCanUnloadNow,'DllCanUnloadNow',\
	DllGetClassObject,'DllGetClassObject',\
	DllRegisterServer,'DllRegisterServer',\
	DllUnregisterServer,'DllUnregisterServer'

;################################################################

section '.reloc' fixups data discardable

;################################################################