wine/dlls/atl100/atl.c

732 lines
22 KiB
C

/*
* Copyright 2012 Stefan Leichter
* Copyright 2012 Jacek Caban for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "atlbase.h"
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(atl100);
static inline void *heap_alloc(size_t len)
{
return HeapAlloc(GetProcessHeap(), 0, len);
}
static inline BOOL heap_free(void *mem)
{
return HeapFree(GetProcessHeap(), 0, mem);
}
static ICatRegister *catreg;
/***********************************************************************
* AtlAdvise [atl100.@]
*/
HRESULT WINAPI AtlAdvise(IUnknown *pUnkCP, IUnknown *pUnk, const IID *iid, DWORD *pdw)
{
IConnectionPointContainer *container;
IConnectionPoint *cp;
HRESULT hres;
TRACE("%p %p %p %p\n", pUnkCP, pUnk, iid, pdw);
hres = IUnknown_QueryInterface(pUnkCP, &IID_IConnectionPointContainer, (void**)&container);
if(FAILED(hres))
return hres;
hres = IConnectionPointContainer_FindConnectionPoint(container, iid, &cp);
IConnectionPointContainer_Release(container);
if(FAILED(hres))
return hres;
hres = IConnectionPoint_Advise(cp, pUnk, pdw);
IConnectionPoint_Release(cp);
return hres;
}
/***********************************************************************
* AtlUnadvise [atl100.@]
*/
HRESULT WINAPI AtlUnadvise(IUnknown *pUnkCP, const IID *iid, DWORD dw)
{
IConnectionPointContainer *container;
IConnectionPoint *cp;
HRESULT hres;
TRACE("%p %p %d\n", pUnkCP, iid, dw);
hres = IUnknown_QueryInterface(pUnkCP, &IID_IConnectionPointContainer, (void**)&container);
if(FAILED(hres))
return hres;
hres = IConnectionPointContainer_FindConnectionPoint(container, iid, &cp);
IConnectionPointContainer_Release(container);
if(FAILED(hres))
return hres;
hres = IConnectionPoint_Unadvise(cp, dw);
IConnectionPoint_Release(cp);
return hres;
}
/***********************************************************************
* AtlFreeMarshalStream [atl100.@]
*/
HRESULT WINAPI AtlFreeMarshalStream(IStream *stm)
{
FIXME("%p\n", stm);
return S_OK;
}
/***********************************************************************
* AtlMarshalPtrInProc [atl100.@]
*/
HRESULT WINAPI AtlMarshalPtrInProc(IUnknown *pUnk, const IID *iid, IStream **pstm)
{
FIXME("%p %p %p\n", pUnk, iid, pstm);
return E_FAIL;
}
/***********************************************************************
* AtlUnmarshalPtr [atl100.@]
*/
HRESULT WINAPI AtlUnmarshalPtr(IStream *stm, const IID *iid, IUnknown **ppUnk)
{
FIXME("%p %p %p\n", stm, iid, ppUnk);
return E_FAIL;
}
/***********************************************************************
* AtlCreateTargetDC [atl100.@]
*/
HDC WINAPI AtlCreateTargetDC( HDC hdc, DVTARGETDEVICE *dv )
{
static const WCHAR displayW[] = {'d','i','s','p','l','a','y',0};
const WCHAR *driver = NULL, *device = NULL, *port = NULL;
DEVMODEW *devmode = NULL;
TRACE( "(%p, %p)\n", hdc, dv );
if (dv)
{
if (dv->tdDriverNameOffset) driver = (WCHAR *)((char *)dv + dv->tdDriverNameOffset);
if (dv->tdDeviceNameOffset) device = (WCHAR *)((char *)dv + dv->tdDeviceNameOffset);
if (dv->tdPortNameOffset) port = (WCHAR *)((char *)dv + dv->tdPortNameOffset);
if (dv->tdExtDevmodeOffset) devmode = (DEVMODEW *)((char *)dv + dv->tdExtDevmodeOffset);
}
else
{
if (hdc) return hdc;
driver = displayW;
}
return CreateDCW( driver, device, port, devmode );
}
/***********************************************************************
* AtlHiMetricToPixel [atl100.@]
*/
void WINAPI AtlHiMetricToPixel(const SIZEL* lpHiMetric, SIZEL* lpPix)
{
HDC dc = GetDC(NULL);
lpPix->cx = lpHiMetric->cx * GetDeviceCaps( dc, LOGPIXELSX ) / 100;
lpPix->cy = lpHiMetric->cy * GetDeviceCaps( dc, LOGPIXELSY ) / 100;
ReleaseDC( NULL, dc );
}
/***********************************************************************
* AtlPixelToHiMetric [atl100.@]
*/
void WINAPI AtlPixelToHiMetric(const SIZEL* lpPix, SIZEL* lpHiMetric)
{
HDC dc = GetDC(NULL);
lpHiMetric->cx = 100 * lpPix->cx / GetDeviceCaps( dc, LOGPIXELSX );
lpHiMetric->cy = 100 * lpPix->cy / GetDeviceCaps( dc, LOGPIXELSY );
ReleaseDC( NULL, dc );
}
/***********************************************************************
* AtlComPtrAssign [atl100.@]
*/
IUnknown* WINAPI AtlComPtrAssign(IUnknown** pp, IUnknown *p)
{
TRACE("(%p %p)\n", pp, p);
if (p) IUnknown_AddRef(p);
if (*pp) IUnknown_Release(*pp);
*pp = p;
return p;
}
/***********************************************************************
* AtlComQIPtrAssign [atl100.@]
*/
IUnknown* WINAPI AtlComQIPtrAssign(IUnknown** pp, IUnknown *p, REFIID riid)
{
IUnknown *new_p = NULL;
TRACE("(%p %p %s)\n", pp, p, debugstr_guid(riid));
if (p) IUnknown_QueryInterface(p, riid, (void **)&new_p);
if (*pp) IUnknown_Release(*pp);
*pp = new_p;
return new_p;
}
/***********************************************************************
* AtlInternalQueryInterface [atl100.@]
*/
HRESULT WINAPI AtlInternalQueryInterface(void* this, const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject)
{
int i = 0;
HRESULT rc = E_NOINTERFACE;
TRACE("(%p, %p, %s, %p)\n",this, pEntries, debugstr_guid(iid), ppvObject);
if (IsEqualGUID(iid,&IID_IUnknown))
{
TRACE("Returning IUnknown\n");
*ppvObject = ((LPSTR)this+pEntries[0].dw);
IUnknown_AddRef((IUnknown*)*ppvObject);
return S_OK;
}
while (pEntries[i].pFunc != 0)
{
TRACE("Trying entry %i (%s %i %p)\n",i,debugstr_guid(pEntries[i].piid),
pEntries[i].dw, pEntries[i].pFunc);
if (!pEntries[i].piid || IsEqualGUID(iid,pEntries[i].piid))
{
TRACE("MATCH\n");
if (pEntries[i].pFunc == (_ATL_CREATORARGFUNC*)1)
{
TRACE("Offset\n");
*ppvObject = ((LPSTR)this+pEntries[i].dw);
IUnknown_AddRef((IUnknown*)*ppvObject);
return S_OK;
}
else
{
TRACE("Function\n");
rc = pEntries[i].pFunc(this, iid, ppvObject, pEntries[i].dw);
if(rc==S_OK || pEntries[i].piid)
return rc;
}
}
i++;
}
TRACE("Done returning (0x%x)\n",rc);
return rc;
}
/* FIXME: should be in a header file */
typedef struct ATL_PROPMAP_ENTRY
{
LPCOLESTR szDesc;
DISPID dispid;
const CLSID* pclsidPropPage;
const IID* piidDispatch;
DWORD dwOffsetData;
DWORD dwSizeData;
VARTYPE vt;
} ATL_PROPMAP_ENTRY;
/***********************************************************************
* AtlIPersistStreamInit_Load [atl100.@]
*/
HRESULT WINAPI AtlIPersistStreamInit_Load( LPSTREAM pStm, ATL_PROPMAP_ENTRY *pMap,
void *pThis, IUnknown *pUnk)
{
FIXME("(%p, %p, %p, %p)\n", pStm, pMap, pThis, pUnk);
return S_OK;
}
/***********************************************************************
* AtlIPersistStreamInit_Save [atl100.@]
*/
HRESULT WINAPI AtlIPersistStreamInit_Save(LPSTREAM pStm, BOOL fClearDirty,
ATL_PROPMAP_ENTRY *pMap, void *pThis,
IUnknown *pUnk)
{
FIXME("(%p, %d, %p, %p, %p)\n", pStm, fClearDirty, pMap, pThis, pUnk);
return S_OK;
}
/***********************************************************************
* AtlModuleAddTermFunc [atl100.@]
*/
HRESULT WINAPI AtlModuleAddTermFunc(_ATL_MODULE *pM, _ATL_TERMFUNC *pFunc, DWORD_PTR dw)
{
_ATL_TERMFUNC_ELEM *termfunc_elem;
TRACE("(%p %p %ld)\n", pM, pFunc, dw);
termfunc_elem = HeapAlloc(GetProcessHeap(), 0, sizeof(_ATL_TERMFUNC_ELEM));
termfunc_elem->pFunc = pFunc;
termfunc_elem->dw = dw;
termfunc_elem->pNext = pM->m_pTermFuncs;
pM->m_pTermFuncs = termfunc_elem;
return S_OK;
}
/***********************************************************************
* AtlCallTermFunc [atl100.@]
*/
void WINAPI AtlCallTermFunc(_ATL_MODULE *pM)
{
_ATL_TERMFUNC_ELEM *iter = pM->m_pTermFuncs, *tmp;
TRACE("(%p)\n", pM);
while(iter) {
iter->pFunc(iter->dw);
tmp = iter;
iter = iter->pNext;
HeapFree(GetProcessHeap(), 0, tmp);
}
pM->m_pTermFuncs = NULL;
}
/***********************************************************************
* AtlLoadTypeLib [atl100.56]
*/
HRESULT WINAPI AtlLoadTypeLib(HINSTANCE inst, LPCOLESTR lpszIndex,
BSTR *pbstrPath, ITypeLib **ppTypeLib)
{
size_t path_len, index_len;
ITypeLib *typelib = NULL;
WCHAR *path;
HRESULT hres;
static const WCHAR tlb_extW[] = {'.','t','l','b',0};
TRACE("(%p %s %p %p)\n", inst, debugstr_w(lpszIndex), pbstrPath, ppTypeLib);
index_len = lpszIndex ? strlenW(lpszIndex) : 0;
path = heap_alloc((MAX_PATH+index_len)*sizeof(WCHAR) + sizeof(tlb_extW));
if(!path)
return E_OUTOFMEMORY;
path_len = GetModuleFileNameW(inst, path, MAX_PATH);
if(!path_len) {
heap_free(path);
return HRESULT_FROM_WIN32(GetLastError());
}
if(index_len)
memcpy(path+path_len, lpszIndex, (index_len+1)*sizeof(WCHAR));
hres = LoadTypeLib(path, &typelib);
if(FAILED(hres)) {
WCHAR *ptr;
for(ptr = path+path_len-1; ptr > path && *ptr != '\\' && *ptr != '.'; ptr--);
if(*ptr != '.')
ptr = path+path_len;
memcpy(ptr, tlb_extW, sizeof(tlb_extW));
hres = LoadTypeLib(path, &typelib);
}
if(SUCCEEDED(hres)) {
*pbstrPath = SysAllocString(path);
if(!*pbstrPath) {
ITypeLib_Release(typelib);
hres = E_OUTOFMEMORY;
}
}
heap_free(path);
if(FAILED(hres))
return hres;
*ppTypeLib = typelib;
return S_OK;
}
/***********************************************************************
* AtlWinModuleInit [atl100.65]
*/
HRESULT WINAPI AtlWinModuleInit(_ATL_WIN_MODULE *winmod)
{
TRACE("(%p\n", winmod);
if(winmod->cbSize != sizeof(*winmod))
return E_INVALIDARG;
InitializeCriticalSection(&winmod->m_csWindowCreate);
winmod->m_pCreateWndList = NULL;
return S_OK;
}
/***********************************************************************
* AtlWinModuleAddCreateWndData [atl100.43]
*/
void WINAPI AtlWinModuleAddCreateWndData(_ATL_WIN_MODULE *pM, _AtlCreateWndData *pData, void *pvObject)
{
TRACE("(%p, %p, %p)\n", pM, pData, pvObject);
pData->m_pThis = pvObject;
pData->m_dwThreadID = GetCurrentThreadId();
EnterCriticalSection(&pM->m_csWindowCreate);
pData->m_pNext = pM->m_pCreateWndList;
pM->m_pCreateWndList = pData;
LeaveCriticalSection(&pM->m_csWindowCreate);
}
/***********************************************************************
* AtlWinModuleExtractCreateWndData [atl100.44]
*/
void* WINAPI AtlWinModuleExtractCreateWndData(_ATL_WIN_MODULE *winmod)
{
_AtlCreateWndData *iter, *prev = NULL;
DWORD thread_id;
TRACE("(%p)\n", winmod);
thread_id = GetCurrentThreadId();
EnterCriticalSection(&winmod->m_csWindowCreate);
for(iter = winmod->m_pCreateWndList; iter && iter->m_dwThreadID != thread_id; iter = iter->m_pNext)
prev = iter;
if(iter) {
if(prev)
prev->m_pNext = iter->m_pNext;
else
winmod->m_pCreateWndList = iter->m_pNext;
}
LeaveCriticalSection(&winmod->m_csWindowCreate);
return iter ? iter->m_pThis : NULL;
}
/***********************************************************************
* AtlComModuleGetClassObject [atl100.15]
*/
HRESULT WINAPI AtlComModuleGetClassObject(_ATL_COM_MODULE *pm, REFCLSID rclsid, REFIID riid, void **ppv)
{
_ATL_OBJMAP_ENTRY **iter;
HRESULT hres;
TRACE("(%p %s %s %p)\n", pm, debugstr_guid(rclsid), debugstr_guid(riid), ppv);
if(!pm)
return E_INVALIDARG;
for(iter = pm->m_ppAutoObjMapFirst; iter < pm->m_ppAutoObjMapLast; iter++) {
if(IsEqualCLSID((*iter)->pclsid, rclsid) && (*iter)->pfnGetClassObject) {
if(!(*iter)->pCF)
hres = (*iter)->pfnGetClassObject((*iter)->pfnCreateInstance, &IID_IUnknown, (void**)&(*iter)->pCF);
if((*iter)->pCF)
hres = IUnknown_QueryInterface((*iter)->pCF, riid, ppv);
TRACE("returning %p (%08x)\n", *ppv, hres);
return hres;
}
}
WARN("Class %s not found\n", debugstr_guid(rclsid));
return CLASS_E_CLASSNOTAVAILABLE;
}
/***********************************************************************
* AtlComModuleUnregisterServer [atl100.22]
*/
HRESULT WINAPI AtlComModuleUnregisterServer(_ATL_COM_MODULE *mod, BOOL bRegTypeLib, const CLSID *clsid)
{
const struct _ATL_CATMAP_ENTRY *catmap;
_ATL_OBJMAP_ENTRY **iter;
HRESULT hres;
TRACE("(%p %x %s)\n", mod, bRegTypeLib, debugstr_guid(clsid));
for(iter = mod->m_ppAutoObjMapFirst; iter < mod->m_ppAutoObjMapLast; iter++) {
if(!*iter || (clsid && !IsEqualCLSID((*iter)->pclsid, clsid)))
continue;
TRACE("Unregistering clsid %s\n", debugstr_guid((*iter)->pclsid));
catmap = (*iter)->pfnGetCategoryMap();
if(catmap) {
hres = AtlRegisterClassCategoriesHelper((*iter)->pclsid, catmap, FALSE);
if(FAILED(hres))
return hres;
}
hres = (*iter)->pfnUpdateRegistry(FALSE);
if(FAILED(hres))
return hres;
}
if(bRegTypeLib) {
ITypeLib *typelib;
TLIBATTR *attr;
BSTR path;
hres = AtlLoadTypeLib(mod->m_hInstTypeLib, NULL, &path, &typelib);
if(FAILED(hres))
return hres;
SysFreeString(path);
hres = ITypeLib_GetLibAttr(typelib, &attr);
if(SUCCEEDED(hres))
hres = UnRegisterTypeLib(&attr->guid, attr->wMajorVerNum, attr->wMinorVerNum, attr->lcid, attr->syskind);
ITypeLib_Release(typelib);
if(FAILED(hres))
return hres;
}
return S_OK;
}
/***********************************************************************
* AtlRegisterClassCategoriesHelper [atl100.49]
*/
HRESULT WINAPI AtlRegisterClassCategoriesHelper(REFCLSID clsid, const struct _ATL_CATMAP_ENTRY *catmap, BOOL reg)
{
const struct _ATL_CATMAP_ENTRY *iter;
HRESULT hres;
TRACE("(%s %p %x)\n", debugstr_guid(clsid), catmap, reg);
if(!catmap)
return S_OK;
if(!catreg) {
ICatRegister *new_catreg;
hres = CoCreateInstance(&CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER,
&IID_ICatRegister, (void**)&new_catreg);
if(FAILED(hres))
return hres;
if(InterlockedCompareExchangePointer((void**)&catreg, new_catreg, NULL))
ICatRegister_Release(new_catreg);
}
for(iter = catmap; iter->iType != _ATL_CATMAP_ENTRY_END; iter++) {
CATID catid = *iter->pcatid; /* For stupid lack of const in ICatRegister declaration. */
if(iter->iType == _ATL_CATMAP_ENTRY_IMPLEMENTED) {
if(reg)
hres = ICatRegister_RegisterClassImplCategories(catreg, clsid, 1, &catid);
else
hres = ICatRegister_UnRegisterClassImplCategories(catreg, clsid, 1, &catid);
}else {
if(reg)
hres = ICatRegister_RegisterClassReqCategories(catreg, clsid, 1, &catid);
else
hres = ICatRegister_UnRegisterClassReqCategories(catreg, clsid, 1, &catid);
}
if(FAILED(hres))
return hres;
}
if(!reg) {
WCHAR reg_path[256] = {'C','L','S','I','D','\\'}, *ptr = reg_path+6;
static const WCHAR implemented_catW[] =
{'I','m','p','l','e','m','e','n','t','e','d',' ','C','a','t','e','g','o','r','i','e','s',0};
static const WCHAR required_catW[] =
{'R','e','q','u','i','r','e','d',' ','C','a','t','e','g','o','r','i','e','s',0};
ptr += StringFromGUID2(clsid, ptr, 64)-1;
*ptr++ = '\\';
memcpy(ptr, implemented_catW, sizeof(implemented_catW));
RegDeleteKeyW(HKEY_CLASSES_ROOT, reg_path);
memcpy(ptr, required_catW, sizeof(required_catW));
RegDeleteKeyW(HKEY_CLASSES_ROOT, reg_path);
}
return S_OK;
}
/***********************************************************************
* AtlWaitWithMessageLoop [atl100.24]
*/
BOOL WINAPI AtlWaitWithMessageLoop(HANDLE handle)
{
MSG msg;
DWORD res;
TRACE("(%p)\n", handle);
while(1) {
res = MsgWaitForMultipleObjects(1, &handle, FALSE, INFINITE, QS_ALLINPUT);
switch(res) {
case WAIT_OBJECT_0:
return TRUE;
case WAIT_OBJECT_0+1:
if(GetMessageW(&msg, NULL, 0, 0) < 0)
return FALSE;
TranslateMessage(&msg);
DispatchMessageW(&msg);
break;
default:
return FALSE;
}
}
}
static HRESULT get_default_source(ITypeLib *typelib, const CLSID *clsid, IID *iid)
{
ITypeInfo *typeinfo, *src_typeinfo = NULL;
TYPEATTR *attr;
int type_flags;
unsigned i;
HRESULT hres;
hres = ITypeLib_GetTypeInfoOfGuid(typelib, clsid, &typeinfo);
if(FAILED(hres))
return hres;
hres = ITypeInfo_GetTypeAttr(typeinfo, &attr);
if(FAILED(hres)) {
ITypeInfo_Release(typeinfo);
return hres;
}
for(i=0; i < attr->cImplTypes; i++) {
hres = ITypeInfo_GetImplTypeFlags(typeinfo, i, &type_flags);
if(SUCCEEDED(hres) && type_flags == (IMPLTYPEFLAG_FSOURCE|IMPLTYPEFLAG_FDEFAULT)) {
HREFTYPE ref;
hres = ITypeInfo_GetRefTypeOfImplType(typeinfo, i, &ref);
if(SUCCEEDED(hres))
hres = ITypeInfo_GetRefTypeInfo(typeinfo, ref, &src_typeinfo);
break;
}
}
ITypeInfo_ReleaseTypeAttr(typeinfo, attr);
ITypeInfo_Release(typeinfo);
if(FAILED(hres))
return hres;
if(!src_typeinfo) {
*iid = IID_NULL;
return S_OK;
}
hres = ITypeInfo_GetTypeAttr(src_typeinfo, &attr);
if(SUCCEEDED(hres)) {
*iid = attr->guid;
ITypeInfo_ReleaseTypeAttr(src_typeinfo, attr);
}
ITypeInfo_Release(src_typeinfo);
return hres;
}
/***********************************************************************
* AtlGetObjectSourceInterface [atl100.54]
*/
HRESULT WINAPI AtlGetObjectSourceInterface(IUnknown *unk, GUID *libid, IID *iid, unsigned short *major, unsigned short *minor)
{
IProvideClassInfo2 *classinfo;
ITypeInfo *typeinfo;
ITypeLib *typelib;
IPersist *persist;
IDispatch *disp;
HRESULT hres;
TRACE("(%p %p %p %p %p)\n", unk, libid, iid, major, minor);
hres = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)&disp);
if(FAILED(hres))
return hres;
hres = IDispatch_GetTypeInfo(disp, 0, 0, &typeinfo);
IDispatch_Release(disp);
if(FAILED(hres))
return hres;
hres = ITypeInfo_GetContainingTypeLib(typeinfo, &typelib, 0);
ITypeInfo_Release(typeinfo);
if(SUCCEEDED(hres)) {
TLIBATTR *attr;
hres = ITypeLib_GetLibAttr(typelib, &attr);
if(SUCCEEDED(hres)) {
*libid = attr->guid;
*major = attr->wMajorVerNum;
*minor = attr->wMinorVerNum;
ITypeLib_ReleaseTLibAttr(typelib, attr);
}else {
ITypeLib_Release(typelib);
}
}
if(FAILED(hres))
return hres;
hres = IUnknown_QueryInterface(unk, &IID_IProvideClassInfo2, (void**)&classinfo);
if(SUCCEEDED(hres)) {
hres = IProvideClassInfo2_GetGUID(classinfo, GUIDKIND_DEFAULT_SOURCE_DISP_IID, iid);
IProvideClassInfo2_Release(classinfo);
ITypeLib_Release(typelib);
return hres;
}
hres = IUnknown_QueryInterface(unk, &IID_IPersist, (void**)&persist);
if(SUCCEEDED(hres)) {
CLSID clsid;
hres = IPersist_GetClassID(persist, &clsid);
if(SUCCEEDED(hres))
hres = get_default_source(typelib, &clsid, iid);
IPersist_Release(persist);
}
return hres;
}
/***********************************************************************
* AtlGetVersion [atl100.@]
*/
DWORD WINAPI AtlGetVersion(void *pReserved)
{
return _ATL_VER;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
switch(fdwReason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDLL);
break;
case DLL_PROCESS_DETACH:
if(catreg)
ICatRegister_Release(catreg);
}
return TRUE;
}