Dll -> service
От: Glоbus Украина  
Дата: 29.03.10 14:45
Оценка:
Товарищи, есть проблема следующего характера.
У меня есть легаси-код, из которого мне требуется сделать СОМ-сервис. Использую для этого дела класс, отнаследованный от CComModule. В результате при компиляции получаю ошибку вида:

error LNK2019: unresolved external symbol "public: void * __thiscall ATL::CAtlBaseModule::GetHInstanceAt(int) referenced in function "void * __cdecl ATL::AtlFindStringResourceInstance(unsigned int,unsigned short)"

К сожалению, в гугле нашел пока только одну ссылку на такую проблему, и то без решения. Подскажите пожалуйста, в чем тут проблема и как ее решить.
Удачи тебе, браток!
Re: Dll -> service
От: Rakafon Украина http://rakafon.blogspot.com/
Дата: 29.03.10 15:28
Оценка: :)
Здравствуйте, Glоbus, Вы писали:
G>Использую для этого дела класс, отнаследованный от CComModule.

Как именно Вы его используете?
"Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
Re[2]: Dll -> service
От: Glоbus Украина  
Дата: 29.03.10 16:18
Оценка:
Здравствуйте, Rakafon, Вы писали:

R>Здравствуйте, Glоbus, Вы писали:

G>>Использую для этого дела класс, отнаследованный от CComModule.

R>Как именно Вы его используете?


Код (в урезанном виде — только основное) выглядит так:

CServiceModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
    OBJECT_ENTRY(CLSID_CoCVShell, CCVShell)
END_OBJECT_MAP()

extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */,
    LPTSTR lpCmdLine, int /* nShowCmd */)
{
    g_hInst = hInstance;
    _Module.Init( ObjectMap, hInstance, IDS_SERVICENAME );
    _Module.m_bService = TRUE;

    TCHAR szTokens[] = _T("-/");
    TCHAR *szContext;
    LPTSTR lpszToken = _tcstok_s(lpCmdLine, szTokens, &szContext);
    while (lpszToken != NULL)
    {
        if (_tcsicmp(lpszToken, _T("UnregServer"))==0)
            return _Module.UnregisterServer();

        if (_tcsicmp(lpszToken, _T("RegServer"))==0)
            return _Module.RegisterServer(TRUE);

        if (_tcsicmp(lpszToken, _T("Embedding"))==0)
            _Module.m_bService = FALSE;

        if (_tcsicmp(lpszToken, _T("LocalServer"))==0)
        {
            _Module.SetupAsLocalServer( hInstance );
            return 0;
        }
        lpszToken = _tcstok_s(NULL, szTokens, &szContext);
    }
    _Module.Start();

    // When we get here, the service has been stopped
    return _Module.m_status.dwWin32ExitCode;
}
Удачи тебе, браток!
Re[2]: Добавка
От: Glоbus Украина  
Дата: 29.03.10 16:44
Оценка:
Здравствуйте, Rakafon, Вы писали:

R>Здравствуйте, Glоbus, Вы писали:

G>>Использую для этого дела класс, отнаследованный от CComModule.

R>Как именно Вы его используете?


Код CServiceModule специально взял из работающего примера

HRESULT CServiceModule::RegisterServer(BOOL bRegTypeLib)
{
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        return hr;
    }//if

    Uninstall();
    UpdateRegistryFromResource( IDR_CVSHELLSRV, TRUE);
    Install();
    HRESULT hRes = RegisterServer(bRegTypeLib);

    CoUninitialize();
    return hRes;
}

 HRESULT CServiceModule::UnregisterServer()
{
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
        return hr;

    UpdateRegistryFromResource(IDR_CVSHELLSRV, FALSE);
    Uninstall();
    UnregisterServer();

    CoUninitialize();
    return S_OK;
}

void CServiceModule::Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, UINT nServiceNameID)
{
    m_bService = TRUE;

    CComModule::Init( p, h );

    LoadString(h, nServiceNameID, m_szServiceName, sizeof(m_szServiceName) / sizeof(TCHAR));

    m_hServiceStatus = NULL;
    m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    m_status.dwCurrentState = SERVICE_STOPPED;
    m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    m_status.dwWin32ExitCode = 0;
    m_status.dwServiceSpecificExitCode = 0;
    m_status.dwCheckPoint = 0;
    m_status.dwWaitHint = 0;
}

LONG CServiceModule::Unlock()
{
    LONG l = __super::Unlock();
    if (l == 0 && !m_bService)
        PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
    return l;
}

BOOL CServiceModule::IsInstalled()
{
    BOOL bResult = FALSE;

    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

    if (hSCM != NULL)
    {
        SC_HANDLE hService = ::OpenService(hSCM, m_szServiceName, SERVICE_QUERY_CONFIG);
        if (hService != NULL)
        {
            bResult = TRUE;
            ::CloseServiceHandle(hService);
        }
        ::CloseServiceHandle(hSCM);
    }
    return bResult;
}

 BOOL CServiceModule::Install()
{
    if (IsInstalled())
        return TRUE;

    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCM == NULL)
    {
        return FALSE;
    }

    // Get the executable file path
    TCHAR szFilePath[_MAX_PATH];
    DWORD dwFLen = ::GetModuleFileName(NULL, szFilePath, _MAX_PATH);
    if( dwFLen == 0 || dwFLen == MAX_PATH )
    {
        return FALSE;
    }

    SC_HANDLE hService = ::CreateService( hSCM, m_szServiceName, m_szServiceName,
                            SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
                            SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
                            szFilePath, NULL, NULL, _T("RPCSS\0"), NULL, NULL);

    if (hService == NULL)
    {
        ::CloseServiceHandle(hSCM);
        return FALSE;
    }

    ::CloseServiceHandle(hService);
    ::CloseServiceHandle(hSCM);
    return TRUE;
}

 BOOL CServiceModule::Uninstall()
{
    if (!IsInstalled())
    {
        return TRUE;
    }

    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

    if (hSCM == NULL)
    {
        return FALSE;
    }

    SC_HANDLE hService = ::OpenService(hSCM, m_szServiceName, SERVICE_STOP | DELETE);

    if (hService == NULL)
    {
        ::CloseServiceHandle(hSCM);
        return FALSE;
    }
    SERVICE_STATUS status;
    ::ControlService(hService, SERVICE_CONTROL_STOP, &status);

    BOOL bDelete = ::DeleteService(hService);
    ::CloseServiceHandle(hService);
    ::CloseServiceHandle(hSCM);

    if (bDelete)
    {
        return TRUE;
    }

    return FALSE;
}

void CServiceModule::LogEvent(LPCTSTR pFormat, ...)
{
    TCHAR    chMsg[256];
    HANDLE  hEventSource;
    LPTSTR  lpszStrings[1];
    va_list pArg;

    va_start(pArg, pFormat);
    _vsntprintf_s(chMsg, sizeof(chMsg), 256, pFormat, pArg);
    chMsg[255] = 0;
    va_end(pArg);

    lpszStrings[0] = chMsg;

    if (m_bService)
    {
        
        hEventSource = RegisterEventSource(NULL, m_szServiceName);
        if (hEventSource)
        {
                ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL);
            DeregisterEventSource(hEventSource);
        }
    }
    else
    {
        // As we are not running as a service, just write the error to the console.
        _putts(chMsg);
    }
}


void CServiceModule::Start()
{
    SERVICE_TABLE_ENTRY st[] =
    {
        { m_szServiceName, _ServiceMain },
        { NULL, NULL }
    };
    if (m_bService && !::StartServiceCtrlDispatcher(st))
    {
        m_bService = FALSE;
    }
    if (m_bService == FALSE)
        Run();
}


 void CServiceModule::ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv )
{
    // Register the control request handler
    m_status.dwCurrentState = SERVICE_START_PENDING;
    m_hServiceStatus = RegisterServiceCtrlHandler(m_szServiceName, _Handler);
    if (m_hServiceStatus == NULL)
    {
        LogEvent(_T("Handler not installed"));
        return;
    }
    SetServiceStatus(SERVICE_START_PENDING);

    m_status.dwWin32ExitCode = S_OK;
    m_status.dwCheckPoint = 0;
    m_status.dwWaitHint = 0;

    // When the Run function returns, the service has stopped.
    Run();

    SetServiceStatus(SERVICE_STOPPED);
    LogEvent(_T("Service stopped"));
}

 void CServiceModule::Handler(DWORD dwOpcode)
{
    switch (dwOpcode)
    {
    case SERVICE_CONTROL_STOP:
        SetServiceStatus(SERVICE_STOP_PENDING);
        PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
        break;
    case SERVICE_CONTROL_PAUSE:
        break;
    case SERVICE_CONTROL_CONTINUE:
        break;
    case SERVICE_CONTROL_INTERROGATE:
        break;
    case SERVICE_CONTROL_SHUTDOWN:
        break;
    default:
        LogEvent(_T("Bad service request"));
    }
}

void WINAPI CServiceModule::_ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
    _Module.ServiceMain(dwArgc, lpszArgv);
}
void WINAPI CServiceModule::_Handler(DWORD dwOpcode)
{
    _Module.Handler(dwOpcode);
}

void CServiceModule::SetServiceStatus(DWORD dwState)
{
    m_status.dwCurrentState = dwState;
    ::SetServiceStatus(m_hServiceStatus, &m_status);
}

void CServiceModule::Run()
{
    HRESULT hr;

    _Module.dwThreadID = GetCurrentThreadId();

    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

    ATLASSERT(SUCCEEDED(hr));

    if( SUCCEEDED(hr) )
    {
        CSecurityDescriptor sd;
        sd.InitializeFromThreadToken();

        hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
            RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
        ATLASSERT(SUCCEEDED(hr));

        hr = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
        ATLASSERT(SUCCEEDED(hr));

        if( SUCCEEDED(hr) )
        {
            SetServiceStatus(SERVICE_RUNNING);

            MSG msg;
            while (GetMessage(&msg, 0, 0, 0))
            {
                DispatchMessage(&msg);
            }//while

            _Module.RevokeClassObjects();
        }//if
    }//if

    CoUninitialize();
}


void CServiceModule::SetupAsLocalServer( HINSTANCE h )
{
    USES_CONVERSION;
    CRegKey keyClasses,key;
    LPOLESTR pCLSID;

    Uninstall();

    if (keyClasses.Open(HKEY_CLASSES_ROOT, _T("CLSID")) != ERROR_SUCCESS)
    {
        return;
    }//if

    _ATL_OBJMAP_ENTRY* pEntry = m_pObjMap;
    while (pEntry->pclsid != NULL)
    {
        StringFromCLSID(*pEntry->pclsid, &pCLSID);
        CString csTemp = pCLSID;
        if (key.Open(keyClasses, (LPCTSTR)csTemp) == ERROR_SUCCESS)
        {
            TCHAR szModule[_MAX_PATH];
            key.DeleteValue(_T("AppID"));
            key.DeleteValue(_T("_LocalServer32"));
            GetModuleFileName( h , szModule, _MAX_PATH);
            key.SetKeyValue(_T("LocalServer32"), szModule, _T(""));
        }
        pEntry++;
    }
}
Удачи тебе, браток!
Re[3]: Добавка
От: Rakafon Украина http://rakafon.blogspot.com/
Дата: 29.03.10 17:38
Оценка:
Здравствуйте, Glоbus, Вы писали:
G>Код (в урезанном виде — только основное) выглядит так:
G>Код CServiceModule специально взял из работающего примера

Та не все эти простыни значения не имеют. Я имею в виду от кого кто наследуется, что с ним делают в точке входа, какие хидеры врубаются и т.д. Я как-то сразу не обратил внимания, что вы наследуетесь от CComModule. Зачем вы наследуетесь от него?

http://msdn.microsoft.com/en-us/library/bwfd57ew%28VS.80%29.aspx

An instance of CAtlBaseModule named _AtlBaseModule is present in every ATL project, containing a handle to the module instance, a handle to the module containing resources (which by default, are one and the same), and an array of handles to modules providing primary resources. CAtlBaseModule can be safely accessed from multiple threads.

This class replaces the obsolete CComModule class used in earlier versions of ATL.


Впрочем вряд ли проблема в CComModule, но ЕМНИП, то для проектов сервисного типа используется CAtlServiceModuleT как базовый. Вот его лучше и пользовать как базу для своего модуля, ну чиста технической правильности проекта ради :)))

У меня такое ощущение, что у вас вообще не atl-евский проект: глобальная переменная _AtlBaseModule определена в файле atlbase.inl или atlbase.cpp, там же и реализации методов AddResourceInstance, RemoveResourceInstance, GetHInstanceAt и конструктора/деструктора. Чтобы "получить" эту имплементацию надо или статически или динамически слинковаться с библиотекой ATL. Ваши unresolved external symbol'ы могут появиться только, если не слинковаться с ATL. Любой ATL-евский проект сам включает эту опцию. Что у вас в опции "Project -> Properties -> Configuration Properties -> General -> Use of ATL" пробито? Если динамическая линковка, то понятно потребная вам функция будет в ATLXX.dll, если статическая то реализация недостающего вам метода будет валяться в atlbase.inl, который автоматом будет включен файлом atlbase.h, если не стоит опция "Dynamic Link to ATL", т.е. если не будет макроса _ATL_DLL определено. Хидеры atlbase.h и atlcore.h включены в sdtafx'е?

Я бы на вашем месте вообще создал бы сервис с нуля, а ля Hello World COM-Service с фейковым интерфейсом и имплементацией выводящей мессаджбокс с дурацким текстом, чтобы убедиться что всё пашет, а затем переносил бы туда существующий легаси-код с интерфейсами.

Удачи!
"Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
Re[3]: Добавка
От: Rakafon Украина http://rakafon.blogspot.com/
Дата: 30.03.10 08:13
Оценка:
Ну что, Glоbus, получилось у вас зарезолвить недостающие реализации методов класса CAtlBaseModule?
"Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
Re[4]: Добавка
От: Glоbus Украина  
Дата: 30.03.10 10:29
Оценка:
Здравствуйте, Rakafon, Вы писали:

R>Здравствуйте, Glоbus, Вы писали:

G>>Код (в урезанном виде — только основное) выглядит так:
G>>Код CServiceModule специально взял из работающего примера

R>Та не все эти простыни значения не имеют. Я имею в виду от кого кто наследуется, что с ним делают в точке входа, какие хидеры врубаются и т.д. Я как-то сразу не обратил внимания, что вы наследуетесь от CComModule. Зачем вы наследуетесь от него?


R>http://msdn.microsoft.com/en-us/library/bwfd57ew%28VS.80%29.aspx


R>

An instance of CAtlBaseModule named _AtlBaseModule is present in every ATL project, containing a handle to the module instance, a handle to the module containing resources (which by default, are one and the same), and an array of handles to modules providing primary resources. CAtlBaseModule can be safely accessed from multiple threads.

R>This class replaces the obsolete CComModule class used in earlier versions of ATL.


Да, я знаю что он обсолит, но решил сделать так... по старинке

R>Впрочем вряд ли проблема в CComModule, но ЕМНИП, то для проектов сервисного типа используется CAtlServiceModuleT как базовый. Вот его лучше и пользовать как базу для своего модуля, ну чиста технической правильности проекта ради


R>У меня такое ощущение, что у вас вообще не atl-евский проект: глобальная переменная _AtlBaseModule определена в файле atlbase.inl или atlbase.cpp, там же и реализации методов AddResourceInstance, RemoveResourceInstance, GetHInstanceAt и конструктора/деструктора. Чтобы "получить" эту имплементацию надо или статически или динамически слинковаться с библиотекой ATL. Ваши unresolved external symbol'ы могут появиться только, если не слинковаться с ATL. Любой ATL-евский проект сам включает эту опцию. Что у вас в опции "Project -> Properties -> Configuration Properties -> General -> Use of ATL" пробито?


Там стоит "Dynamic Link...". Хотя можно ставить и статик — это ситуацию не меняет — все равно кидает анрезолвед экстёрнал.

R>Если динамическая линковка, то понятно потребная вам функция будет в ATLXX.dll, если статическая то реализация недостающего вам метода будет валяться в atlbase.inl, который автоматом будет включен файлом atlbase.h, если не стоит опция "Dynamic Link to ATL", т.е. если не будет макроса _ATL_DLL определено. Хидеры atlbase.h и atlcore.h включены в sdtafx'е?


Да, включены, но не в stdafx, в других файлах.

R>Я бы на вашем месте вообще создал бы сервис с нуля, а ля Hello World COM-Service с фейковым интерфейсом и имплементацией выводящей мессаджбокс с дурацким текстом, чтобы убедиться что всё пашет, а затем переносил бы туда существующий легаси-код с интерфейсами.


Да, я собственно наверное так и сделаю щас

R>Удачи!

R>
Удачи тебе, браток!
Re[4]: Добавка
От: Glоbus Украина  
Дата: 30.03.10 10:31
Оценка:
Здравствуйте, Rakafon, Вы писали:

R>Ну что, Glоbus, получилось у вас зарезолвить недостающие реализации методов класса CAtlBaseModule?


Ну как я уже говорил, если создать новый, изначально АТЛ-ный прожект, то все пашет нормально Вот сейчас в него легаси перетаскиваю — там просто код 25-летней давности (и это нифига не шутка! таки реально 25 лет!) с кучей всякого мусора, так что приходится чуток исхитряться
Удачи тебе, браток!
Re[5]: Добавка
От: Rakafon Украина http://rakafon.blogspot.com/
Дата: 30.03.10 11:01
Оценка:
Здравствуйте, Glоbus, Вы писали:
G>Ну как я уже говорил, если создать новый, изначально АТЛ-ный прожект, то все пашет нормально :) Вот сейчас в него легаси перетаскиваю — там просто код 25-летней давности (и это нифига не шутка! таки реально 25 лет!) с кучей всякого мусора, так что приходится чуток исхитряться :)
ГЫ :)
"Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.