Onega's profileOnegaBlogListsNetwork Tools Help

Blog


    April 21

    Changing the Startup type and description of VC++ 2008 service project

    #include <stdio.h>
    #include <sstream>
    #include <xutility>
    #include <exception>
    #include <iomanip>
    typedef std::basic_stringstream<TCHAR> tsstream;
    typedef std::basic_string<TCHAR> tstring;
    #ifdef _UNICODE
    #define __FUNCTIONT__ __FUNCTIONW__
    #else
    #define __FUNCTIONT__ __FUNCTION__
    #endif

    class COzSvcModule : public CAtlServiceModuleT< COzSvcModule, IDS_SERVICENAME >
    {
    public :
        DECLARE_LIBID(LIBID_OzSvcLib)
        DECLARE_REGISTRY_APPID_RESOURCEID(IDR_OZSVC, "{08E6E609-0FBD-4098-8006-D703FCEB8C17}")
        HRESULT InitializeSecurity() throw()
        {
            // TODO : Call CoInitializeSecurity and provide the appropriate security settings for
            // your service
            // Suggested - PKT Level Authentication,
            // Impersonation Level of RPC_C_IMP_LEVEL_IDENTIFY
            // and an appropiate Non NULL Security Descriptor.
            CSecurityDescriptor sd;
            sd.InitializeFromThreadToken();
            HRESULT hr = CoInitializeSecurity(sd, -1, NULL, NULL,
                RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

            return hr;
        }
        COzSvcModule()
        {
            m_status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
        }
        HRESULT RegisterAppId(bool bService=false)
        {
            HRESULT hr = S_OK;
            hr = __super::RegisterAppId(bService);
            if (!bService)
                return hr;
            //Changing the Startup type and description of the service
            SC_HANDLE hSCM = NULL;
            SC_LOCK sclLock = NULL;
            SC_HANDLE hService = NULL;
            try
            {
                hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
                if (hSCM == NULL)
                {
                    throw std::runtime_error("Couldn't open service manager");
                }
                sclLock = LockServiceDatabase(hSCM);
                if(sclLock == NULL)
                {
                    throw std::runtime_error("Couldn't Lock Service Database");
                }

                hService = OpenService(
                    hSCM, // SCManager database
                    m_szServiceName, // name of service
                    SERVICE_CHANGE_CONFIG); // need CHANGE access
                if (hService == NULL)
                {
                    throw std::runtime_error("Couldn't Open Service");
                }

                if (! ChangeServiceConfig(
                    hService, // handle of service
                    SERVICE_WIN32_OWN_PROCESS, // service type: no change
                    SERVICE_AUTO_START, // change service start type
                    SERVICE_NO_CHANGE, // error control: no change
                    NULL, // binary path: no change
                    NULL, // load order group: no change
                    NULL, // tag ID: no change
                    NULL, // dependencies: no change
                    NULL, // account name: no change
                    NULL, // password: no change
                    NULL) ) // display name: no change
                {
                    throw std::runtime_error("Couldn't Change Service Config");
                }
                // To Change the description of the service
                SERVICE_DESCRIPTION sdBuf;
                memset(&sdBuf, 0, sizeof(sdBuf));
                sdBuf.lpDescription = _T("Description of My Auto Start Service");
                if( !ChangeServiceConfig2(
                    hService, // handle to service
                    SERVICE_CONFIG_DESCRIPTION, // change: description
                    &sdBuf) ) // value: new description
                {
                    throw std::runtime_error("Change Service Config Failed");
                }
            }
            catch (const std::runtime_error& e)
            {
                hr = HRESULT_FROM_WIN32( GetLastError());
                std::stringstream ss;
                ss << "Error code:" << std::hex << std::setfill('0') << std::setw(8) << std::internal
                    << hr << ", description: " << e.what();
                OutputDebugStringA(ss.str().c_str());
            }
            if (sclLock)
                UnlockServiceDatabase(sclLock);
            if (hService)
                ::CloseServiceHandle(hService);
            if (hSCM)
                ::CloseServiceHandle(hSCM);
            return hr;
        }
    };

    add

    #define _ATL_NO_COM_SUPPORT to stdafx.h for NON-COM project.

    To register the service, pass "-service" parameter, use "-unregserver" to uninstall it.

    January 18

    dynamic_cast with COM interface pointer

    When I use c cast to get a pointer to an implementation class of an interface and call its method, it reports access violation upon the entry of the method, even if the method only contains one line: "return S_OK". In debugger I can see that some member variables are correctly initialized, but a few variables are unable to be evaluated by debugger. My first guess is that ctor of its parent is not called, so I manually added the call to initialization sequence of ctor of the implementation class, but it is not the fix. After some try and error process, it is solved by dynamic_cast.

    Environment: VC++ 2005 (without SP1).

    class CMyInterface: public CParentClass, // yes, it inherits from another class
        public CComObjectRootEx<CComMultiThreadModelNoCS>,
        public CComCoClass<CMyInterface, CLSID_MyInterface>,
        public IDispatchImpl<IMyInterface, &IID_IMyInterface, &LIBID_MyServer>
    {
    ...
    };

    IMyInterface* p=NULL;
    HRESULT hr = CMyInterface::CreateInstance(&p);
    if (FAILED(hr))
        ...
    CMyInterface* pMe = NULL;
    pMe = (CMyInterface*)p; // this does not work, it seems that pMe is not correcly aligned.
    pMe = dynamic_cast<CMyInterface*>(p); // this works well
    if (!pMe)
        ...

    When using C cast, some members of CParentClass are visible in debugger, but a few are not!

    November 27

    ROT and typelib

    If typelib is not properly registered, a COM object can be registered to ROT, but subsequent GetObject would fail with error : "Invalid Pointer".

    It is valid to have two typelib in one executable, but the registeration has to be taken manually via CComModule::RegisterTypeLib(LPCTSTR lpszIndex);

    CreateItemMoniker can be used to register a random interface object.

    October 15

    RPC_E_CANTCALLOUT_ININPUTSYNCCALL Error with Out-of-Proc COM Server

    Steps to reproduce:

    Create an out-of-proc COM server, add one method

    STDMETHODIMP ExeMsg::Test(long process_id, long thread_id)
    {
        std::stringstream ss;
        ss  << "ExeMsg::Test() Called from process " << process_id
            << " caller thread " << thread_id
            << " callee process " << GetCurrentProcessId()
            << " callee thread " << GetCurrentThreadId();
        if (InSendMessage())
            ss << " inside SendMessage";
        OutputDebugString(ss.str().c_str());
        return S_OK;
    }

    Create a MFC Dialog based application, add two buttons to it.

    Start a work thread to send a private message back to the main dialog, the private message handler will call method of the out-of-proc COM object.

    ON_MESSAGE(MSG_MYMSG, OnMsg)

    UINT TestSendMsgThreadProc(LPVOID unusedarg)
    {
        SendMessage(theApp.m_pMainWnd->GetSafeHwnd(), MSG_MYMSG, GetCurrentProcessId(), GetCurrentThreadId());
        return 0;
    }

    void CMsgclientDlg::OnButton2()
    {
        AfxBeginThread(TestSendMsgThreadProc, 0);
    }

    struct Parameters
    {
        DWORD proc_id;
        DWORD thread_id;
    };

    UINT COMProcThread(LPVOID pa)
    {
        CoInitialize(NULL);
        Parameters* p = (Parameters*)pa;
        EXEMSGLib::IExeMsgPtr pExeMsg;
        pExeMsg.CreateInstance(__uuidof(EXEMSGLib::ExeMsg));
        HRESULT hr = pExeMsg->Test(p->proc_id, p->thread_id);
        CoUninitialize();
        return hr;
    }

    LRESULT CMsgclientDlg::OnMsg( WPARAM w, LPARAM l )
    {
        pMsg->Test(w, l); // pMsg is an InProc COM object and it is fine to be called inside SendMessage!
        if (InSendMessage())
        { // if uncommented, RPC_E_CANTCALLOUT_ININPUTSYNCCALL Error will occur.
    // EXEMSGLib::IExeMsgPtr pExeMsg;
    // pExeMsg.CreateInstance(__uuidof(EXEMSGLib::ExeMsg));
    // pExeMsg->Test(w, l);

        }
        if (InSendMessage())
        {
            Parameters pa;
            pa.proc_id = w;
            pa.thread_id = l;
            CWinThread* pthread = AfxBeginThread(COMProcThread, &pa);
            WaitForSingleObject(pthread->m_hThread, INFINITE);
        }

        if (!InSendMessage())
        {
            pExeMsg->Test(w, l);
        }

        return 0;
    }

    September 19

    Purify reports memory leak in UpdateRegistryFromResource method

    Rational Purify reported memory leak in UpdateRegistryFromResource method, which is introduced by DECLARE_REGISTRY_RESOURCEID(...) macro. After I rebuilt with _ATL_STATIC_REGISTRY enabled, Rational Purify is happy.
    July 17

    Wrapper class for using UDT (User Defined Type) in COM

    UDTs must be freed by caller, just like BSTR. So I created a template to
    ease the work of caller.
    template <typename UDT >
    class UDTWrapper : public UDT
    {
    public:
    UDTWrapper()
    {
    HRESULT hr = S_OK;
    CComPtr<ITypeLib> pTypelib;
    CComPtr<ITypeInfo> pTypeInfo;
    hr = LoadRegTypeLib(lib_id, 1, 0, GetUserDefaultLCID(), &pTypelib);
    _ASSERT(SUCCEEDED(hr) && pTypelib);
    hr = pTypelib->GetTypeInfoOfGuid(__uuidof(UDT), &pTypeInfo);
    _ASSERT(SUCCEEDED(hr) && pTypeInfo);
    hr = GetRecordInfoFromTypeInfo(pTypeInfo, &pRecInfo);
    pRecInfo->RecordInit(this);
    }
    ~UDTWrapper()
    {
    if (pRecInfo)
    {
    HRESULT hr = pRecInfo->RecordClear(this);
    printf("RecordClear return %08x\n", hr);
    }
    else
    {
    printf("RecInfo is NULL\n");
    }
    }
    private:
    CComPtr<IRecordInfo> pRecInfo;
    public:
    static GUID lib_id;

    };

    template <class UDT> GUID UDTWrapper<UDT>::lib_id;

    typedef UDTWrapper<UDT1Lib::sSiteStatus> sSiteStatusWrapper;
    typedef UDTWrapper<UDT1Lib::sStationStatus> sStationStatusWrapper;

    int main(int argc, char* argv[])
    {
    CoInitialize(0);
    HRESULT hr = S_OK;
    UDTWrapper<UDT1Lib::sSiteStatus>::lib_id = __uuidof(UDT1Lib::__UDT1Lib);
    UDTWrapper<UDT1Lib::sStationStatus>::lib_id =
    __uuidof(UDT1Lib::__UDT1Lib);

    hr = TestGetSingleUdt();
    //hr = TestGetStationStatus();
    //hr = TestSiteStatusWrapper();
    //hr = TestStationStatusWrapper();
    printf("TestGetSingleUdt return %08x!\n", hr);
    system("Pause");
    return 0;
    }

    VC++ 6.0 does not associate GUID with UDTs in tlh file, so I need to do
    the following workaround.
    #if _MSC_VER < 1400
    // VC++ 2005 will associate GUID with used defined structures and type
    library.
    // but VC6 does not do this.
    namespace UDT1Lib{
    struct __declspec(uuid("95d6c27d-baab-41e0-aeee-cd30e0b15bba"))
    __UDT1Lib;
    struct __declspec(uuid("3caf4043-6af3-4a2f-9518-77b6bd0b37e9"))
    sSiteStatus;
    struct __declspec(uuid("d1b94033-31c9-4623-9d4f-5278ae9a682a"))
    sStationStatus;
    }
    #endif



    July 01

    COM Client Method Call Fails with ERROR_NOACCESS(0xC0000005)

    I encountered this problem in recent project development. It is very likely to be the same problem as documented in MSDN Q273721. Only one method failed, because it contains a parameter of IUnknown*. Another related KB article may be Q149231 Marshaling Code for Connection Point Interfaces.
    Solution: change method signature to use VARIANT instead of LPUNKNOWN/LPDISPATCH.
    June 21

    Share COM objects between threads/processes

    The CoCreateFreeThreadedMarshaler function enables an object to efficiently marshal interface pointers between threads in the same process.
    The IGlobalInterfaceTable interface is an efficient way for a process to store an interface pointer in a memory location that can be accessed from multiple apartments within the process, such as processwide variables and agile (free-threaded marshaled) objects containing interface pointers to other objects.
    The IRunningObjectTable interface manages access to the Running Object Table (ROT), a globally accessible look-up table on each workstation.
    May 14

    Add ATL object to existing MFC SDI project

    I met the following mistakes:
    missing [out] specifier in IDL file, so the method can't be called by
    vbscript;
    messing up CLSID and LIBID.
    AfxGetApp() and AfxGetMainWnd() return NULL -- workaround is to use
    theApp object, store HWND as a member of theApp class, initial it in
    theApp.InitInstance().

    I also add connection point support to this project, but sink can't register to the interface because QueryInterface(IID_MyEventInterface, ...) failed with error code 0x80004002, interestingly QueryInterface(IID_IDispatch,...) is successful. Maybe that is why event source usually access client via IDispatch interface. After spending many hours diagnosing the problem, I found that the event interface shall also be registered in the event source project like other COM interface, always the event interface does not have its coclass.
    HKCR
    {
        CallbackServer.CallbackTest.1 = s 'CallbackTest Class'
        {
            CLSID = s '{AB2E5112-5FE5-49ED-A60E-DADAC49DFF05}'
        }
        CallbackServer.CallbackTest = s 'CallbackTest Class'
        {
            CLSID = s '{AB2E5112-5FE5-49ED-A60E-DADAC49DFF05}'
            CurVer = s 'CallbackServer.CallbackTest.1'
        }
        NoRemove CLSID
        {
            ForceRemove {AB2E5112-5FE5-49ED-A60E-DADAC49DFF05} = s 'CallbackTest Class'
            {
                ProgID = s 'CallbackServer.CallbackTest.1'
                VersionIndependentProgID = s 'CallbackServer.CallbackTest'
                ForceRemove 'Programmable'
                LocalServer32 = s '%MODULE%'
                val AppID = s '%APPID%'
                'TypeLib' = s '{EC8F9F17-3EB2-42E0-A73C-F5CD74D77377}'
            }
        }
    }



    The hint is from a post here: http://www.codeproject.com/atl/conexe.asp