HOWTO: Office XP에서 사용할 스마트 태그 DLL 만들기

기술 자료 번역 기술 자료 번역
기술 자료: 292596 - 이 문서가 적용되는 제품 보기.
이 문서는 이전에 다음 ID로 출판되었음: KR292596
이 문서가 보관되었습니다. "그대로" 제공되었으며, 업데이트가 되지 않을 것입니다.
모두 확대 | 모두 축소

이 페이지에서

요약

스마트 태그는 Office XP에 소개된 기술로, Office 사용자에게 Office 문서 내용을 대화형으로 작업할 수 있는 기능을 제공합니다. 스마트 태그는 Office 문서의 텍스트 요소로, 이와 연관된 사용자 지정 작업이 있는 것으로 인식됩니다. 이러한 특수한 텍스트 요소의 한 예로 Word 문서나 Excel 통합 문서에 입력된 전자 메일 이름을 들 수 있습니다. 전자 메일 이름이 스마트 태그로 인식되면 그 텍스트에 수행할 하나 이상의 작업이 제공됩니다. 전자 메일 이름과 연관된 가능한 작업은 추가 연락처 정보를 찾거나 새 전자 메일 메시지를 해당 연락처로 보내는 것입니다.

Office 문서에 사용할 스마트 태그 인식자/동적 연결 라이브러리(DLL)를 직접 개발하여 Office XP의 기능을 확장할 수 있습니다. 이 문서에서는 Active Template Library(ATL)를 사용하여 스마트 태그 DLL을 작성하는 방법과 스마트 태그 DLL을 식별하고 사용하기 위해 Office XP에 필요한 레지스트리 설정에 대해 설명합니다.

참고 : Excel 2002와 Word 2002는 스마트 태그를 지원하는 유일한 Office XP 응용 프로그램입니다. 그러나, 이 문서에 제공된 정보를 스마트 태그 기술을 사용한 응용 프로그램의 스마트 태그 개발에 적용할 수 있습니다. 스마트 태그 DLL은 ISmartTagRecognizerISmartTagAction 인터페이스를 구현한 표준 구성 요소 개체 모델(COM) DLL입니다.. ISmartTagRecognizer 인터페이스는 문서에 입력된 텍스트를 스마트 태그로 인식합니다. ISmartTagAction 인터페이스는 사용자 요청에 대해 특정 스마트 태그 문자열에 작업을 수행합니다. 이러한 인터페이스는 같은 DLL에 구현하지 않아도 됩니다. 여러 작업에 대해 인식자 DLL 하나와 단일 스마트 태그 유형을 확장한 작업 DLL이 하나 이상 있을 수 있습니다.




ATL에서 스마트 태그 DLL을 만드는 단계


다음 단계에서는 Microsoft Network(MSN) Instant Messenger 연락처를 인식하고 사용자에게 전자 메일이나 인스턴트 메시지를 인식된 연락처로 보내는 기능을 제공하는 간단한 스마트 태그 DLL을 만듭니다. Instant Messenger가 있어야 이 예제를 사용할 수 있습니다. Instant Messenger가 없을 경우 아래 MSN 웹 사이트에서 복사본을 얻을 수 있습니다.
http://messenger.msn.com/

  1. Visual C++에서 새로운 ATL COM AppWizard 프로젝트를 만듭니다. 프로젝트의 이름을 MessengerSmartTag로 지정합니다.
  2. ATL COM 마법사를 시작하려면 OK 를 누릅니다. 다음 대화 상자에서 Dynamic Link Library 가 선택되어 있는지 확인하고 Finish 를 누릅니다. OK 를 눌러 프로젝트를 만듭니다.
  3. Recognizer 클래스를 만들려면 Insert 메뉴에서 New ATL Object 를 누릅니다. Simple Object 를 선택하고 Next 를 누릅니다. 짧은 이름으로 Recognizer 를 입력하고 OK 를 누릅니다.
  4. Action 클래스를 만들려면 3단계의 지시를 따르고 짧은 이름으로 Action 을 입력합니다.
  5. 클래스 뷰를 열고 MessengerSmartTag classes 를 확장합니다. CRecognizer 클래스를 마우스 오른쪽 단추로 누르고 Implement Interface 를 선택합니다. 경고 대화 상자가 나타나면 OK 를 누릅니다. Browse 를 누르고 Microsoft Smart Tags 1.0 Type Library 를 선택합니다. ISmartTagRecognizer 인터페이스를 선택하고 OK 를 누릅니다.

    참고 : Microsoft Smart Tags 1.0 Type Library의 기본 위치는 C:\Program Files\Common Files\Microsoft Shared\Mstag.tlb입니다.
  6. 클래스 뷰에서 CAction 클래스를 마우스 오른쪽 단추로 누르고 Implement Interface 를 선택합니다. 경고 대화 상자가 나타나면 OK 를 누릅니다. Browse 를 누르고 Microsoft Smart Tags 1.0 Type Library 를 선택합니다. ISmartTagAction 인터페이스를 선택하고 OK 를 누릅니다.
  7. Recognizer.h 파일을 열고 클래스 내용을 다음 코드로 바꿉니다.
    ///////////////////////////////////////////////////////////////////////////// 
    // CRecognizer
    class ATL_NO_VTABLE CRecognizer : 
       public CComObjectRootEx<CComSingleThreadModel>,
       public CComCoClass<CRecognizer, &CLSID_Recognizer>,
       public IDispatchImpl<IRecognizer, &IID_IRecognizer, &LIBID_MESSENGERSMARTTAGLib>,
       public IDispatchImpl<ISmartTagRecognizer, &IID_ISmartTagRecognizer, &LIBID_SmartTagLib>
    {
    public:
    
        CRecognizer();
        ~CRecognizer();
    
    DECLARE_REGISTRY_RESOURCEID(IDR_RECOGNIZER)
    
    DECLARE_PROTECT_FINAL_CONSTRUCT()
    
    BEGIN_COM_MAP(CRecognizer)
        COM_INTERFACE_ENTRY(IRecognizer)
    //Removed -- COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY2(IDispatch, IRecognizer)
        COM_INTERFACE_ENTRY(ISmartTagRecognizer)
    END_COM_MAP()
    
    // IRecognizer
    public:
    // ISmartTagRecognizer
        STDMETHOD(get_ProgId)(BSTR * ProgId);
        STDMETHOD(get_Name)(INT LocaleID, BSTR * Name);
        STDMETHOD(get_Desc)(INT LocaleID, BSTR * Desc);
        STDMETHOD(get_SmartTagCount)(INT * Count);
        STDMETHOD(get_SmartTagName)(INT SmartTagID, BSTR * Name);
        STDMETHOD(get_SmartTagDownloadURL)(INT SmartTagID, BSTR * DownloadURL);
        STDMETHOD(Recognize)(BSTR Text, IF_TYPE DataType, INT LocaleID,
                      ISmartTagRecognizerSite * RecognizerSite);
    private:
        long lCount;
        SAFEARRAY *psa;
    };
  8. Recognizer.cpp 파일을 열고 다음 코드를 파일 끝에 추가합니다.
    CRecognizer::CRecognizer()
    {
        Messenger::IMsgrObject2Ptr oMsgrObj = NULL;
        Messenger::IMsgrUsersPtr oUsers = NULL;
        Messenger::IMsgrUserPtr oUser = NULL;
        SAFEARRAYBOUND rgsaBound[1];
        long rgIndices[1];		
        HRESULT hr;
    		
        // Create an instance of Instant Messenger.
        oMsgrObj.CreateInstance("Messenger.MsgrObject");
        // Get the list of contacts
        oUsers = oMsgrObj->GetList(Messenger::MLIST_CONTACT);
        // Store the number of contacts you have.
        lCount = oUsers->GetCount();
        rgsaBound[0].lLbound = 0;
        rgsaBound[0].cElements = lCount;
        // Create a SAFEARRAY to hold the list of contacts.
        psa = SafeArrayCreate(VT_VARIANT, 1, rgsaBound);
        // Loop through all contacts.
        for (long l=0; l<lCount-1; l++)
        {
            rgIndices[0] = l;	
            // Set the specific user.
            oUser = oUsers->Item(l);
            // Convert the Friendly Name to lower case
            // and store it in a VARIANT.
            _variant_t v = _wcslwr(oUser->GetFriendlyName());
            // Put the VARIANT into the SAFEARRAY.
           hr = SafeArrayPutElement(psa, rgIndices, &v);
        }
    }
    
    CRecognizer::~CRecognizer()
    {
        // Destroy the SAFEARRAY.
        SafeArrayDestroy(psa);
    }
    
    
    HRESULT CRecognizer::get_ProgId(BSTR * ProgId)
    {
        // Set the ProgID of the Recognizer interface.
        *ProgId = SysAllocString(L"MessengerSmartTag.Recognizer");
        return S_OK;
    }
    
    HRESULT CRecognizer::get_Name(INT LocaleID, BSTR * Name)
    {
        // Set a short title about the recognizer.
        *Name = SysAllocString(L"Microsoft Messenger Contacts Visual C++ Recognizer");
        return S_OK;
    }
    
    HRESULT CRecognizer::get_Desc(INT LocaleID, BSTR * Desc)
    {
        // Set a long description of the recognizer.
        *Desc = SysAllocString(L"Microsoft Messenger recognizes your Instant Messenger Contacts");
        return S_OK;
    }
    
    HRESULT CRecognizer::get_SmartTagCount(INT * Count)
    {
        // Set the number of Smart Tags that are supported.
        *Count = 1;
        return S_OK;
    }
    
    HRESULT CRecognizer::get_SmartTagName(INT SmartTagID, BSTR * Name)
    {
        // This method is called the same number of times as you
        // return in SmartTagCount. This method sets a unique name
        // for the Smart Tag.
        *Name = SysAllocString(L"microsoft/messenger#contacts");
        return S_OK;
    }
    
    HRESULT CRecognizer::get_SmartTagDownloadURL(INT SmartTagID, BSTR * DownloadURL)
    {
        // Set the URL that gets embedded in documents.
        *DownloadURL = NULL;
        return S_OK;
    }
    
    HRESULT CRecognizer::Recognize(BSTR Text, IF_TYPE DataType, INT LocaleID,
           ISmartTagRecognizerSite * RecognizerSite)
    {
        // The Recognize method is called and passed a text value.
        // You should recognize strings in the text and set up the actions.
        WCHAR *pch, *strText = _wcslwr(Text);
        ISmartTagProperties  *pSmartTagProp = NULL;
        long rgIndices[1];
        HRESULT hr;
    
        // Look through all contacts
        for (long l = 0; l<lCount; l++).
        {
            rgIndices[0] = l;
    
            // Get the contact name.
            _variant_t v;
            hr = SafeArrayGetElement(psa,rgIndices,&v);
            // Convert the VARIANT to a BSTR.
            _bstr_t bstrContact = v;
            // Loop through the string looking for contacts.
            for (pch = strText; (pch = wcsstr(pch, bstrContact))!=NULL; pch++)
            {    
                // Create a new property bag.
                hr = RecognizerSite->GetNewPropertyBag(&pSmartTagProp);
                if (SUCCEEDED(hr)) {
                    // Commit the Smart Tag to the property bag.		                hr = RecognizerSite->CommitSmartTag(
                          _bstr_t("microsoft/messenger#contacts"),
                          pch - strText+1, wcslen(bstrContact),
                          pSmartTagProp);
                    
                    if (pSmartTagProp != NULL)
                        pSmartTagProp->Release();
                }
            }
        }
        return S_OK;
    }
  9. Action.h 파일을 열고 클래스 내용을 다음 코드로 바꿉니다.
    ///////////////////////////////////////////////////////////////////////////// 
    // CAction
    class ATL_NO_VTABLE CAction : 
       public CComObjectRootEx<CComSingleThreadModel>,
       public CComCoClass<CAction, &CLSID_Action>,
       public IDispatchImpl<IAction, &IID_IAction, &LIBID_MESSENGERSMARTTAGLib>,
       public IDispatchImpl<ISmartTagAction, &IID_ISmartTagAction, &LIBID_SmartTagLib>
    {
    public:
       CAction(){}
    
    DECLARE_REGISTRY_RESOURCEID(IDR_ACTION)
    
    DECLARE_PROTECT_FINAL_CONSTRUCT()
    
    BEGIN_COM_MAP(CAction)
        COM_INTERFACE_ENTRY(IAction)
    //Removed -- COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY2(IDispatch, IAction)
        COM_INTERFACE_ENTRY(ISmartTagAction)
    END_COM_MAP()
    
    // IAction
    public:
    // ISmartTagAction
        STDMETHOD(get_ProgId)(BSTR * ProgId);
        STDMETHOD(get_Name)(INT LocaleID, BSTR * Name);
        STDMETHOD(get_Desc)(INT LocaleID, BSTR * Desc);
        STDMETHOD(get_SmartTagCount)(INT * Count);
        STDMETHOD(get_SmartTagName)(INT SmartTagID, BSTR * Name);
        STDMETHOD(get_SmartTagCaption)(INT SmartTagID, INT LocaleID,
                      BSTR * Caption);
        STDMETHOD(get_VerbCount)(BSTR SmartTagName, INT * Count);
        STDMETHOD(get_VerbID)(BSTR SmartTagName, INT VerbIndex, INT * VerbID);
        STDMETHOD(get_VerbCaptionFromID)(INT VerbID, BSTR ApplicationName,
                      INT LocaleID, BSTR * Caption);
        STDMETHOD(get_VerbNameFromID)(INT VerbID, BSTR * Name);
        STDMETHOD(InvokeVerb)(INT VerbID, BSTR ApplicationName,
                      IDispatch * Target, ISmartTagProperties * Properties,
                      BSTR Text, BSTR Xml);
    };
  10. Action.cpp 파일을 열고 다음 코드를 파일 끝에 추가합니다.
    HRESULT CAction::get_ProgId(BSTR * ProgId)
    {
       // Set the ProgID of the Action interface.
       *ProgId = SysAllocString(L"MessengerSmartTag.Action");
       return S_OK;
    }
    
    HRESULT CAction::get_Name(INT LocaleID, BSTR * Name)
    {
       // Set a short name describing the Action.
       *Name = SysAllocString(L"Messenger Smart Tag");
       return S_OK;
    }
    
    HRESULT CAction::get_Desc(INT LocaleID, BSTR * Desc)
    {
       // Set a long description describing the action.
       *Desc = SysAllocString(L"Provides actions for the Messenger Smart Tag");
       return S_OK;
    }
    
    HRESULT CAction::get_SmartTagCount(INT * Count)
    {
        // Set the number of smart tags this action supports.
        *Count = 1;
        return S_OK;
    }
    
    HRESULT CAction::get_SmartTagName(INT SmartTagID, BSTR * Name)
    {
        // This method is called the same number of times as you
        // return in SmartTagCount. This method sets a unique name
        // for the smart tag.
        *Name = SysAllocString(L"microsoft/messenger#contacts");
        return S_OK;
    }
    
    HRESULT CAction::get_SmartTagCaption(INT SmartTagID, INT LocaleID, BSTR * Caption)
    {
        // This caption is displayed on the menu for the smart tag.
        *Caption = SysAllocString(L"Messenger Smart Tag");
        return S_OK;
    }
    
    HRESULT CAction::get_VerbCount(BSTR SmartTagName, INT * Count)
    {
        // Return the number of verbs we support.
        if (wcsstr(SmartTagName,L"microsoft/messenger#contacts") != 0) {
            *Count = 2;
        }
        return S_OK;
    }
    
    HRESULT CAction::get_VerbID(BSTR SmartTagName, INT VerbIndex, INT * VerbID)
    {
        // Return a unique ID for each verb we support.
        *VerbID = VerbIndex;
        return S_OK;
    }
    
    HRESULT CAction::get_VerbCaptionFromID(INT VerbID, BSTR ApplicationName,
                         INT LocaleID, BSTR * Caption)
    {
        // Set a caption for each verb. This caption is displayed
        // on the Smart Tag menu.
        switch (VerbID) {
          case 1:
            *Caption = SysAllocString(L"Send this contact an Instant Message");
            break;
          case 2:
            *Caption = SysAllocString(L"Send email to this contact");
            break;
          default:
            *Caption = NULL;
            break;
        }
        return S_OK;
    }
    
    HRESULT CAction::get_VerbNameFromID(INT VerbID, BSTR * Name)
    {
        // Set a string name for each verb.
        switch (VerbID) {
          case 1:
            *Name = SysAllocString(L"SendInstantMessage");
            break;
          case 2:
            *Name = SysAllocString(L"SendEmail");
            break;
        }
        return S_OK;
    }
    HRESULT CAction::InvokeVerb(INT VerbID, BSTR ApplicationName,
          IDispatch * Target, ISmartTagProperties * Properties,
          BSTR Text, BSTR Xml)
    {
        // This method is called when a user invokes a verb
        // from the Smart Tag menu.
        Messenger::IMessengerApp2Ptr oMessenger = NULL;
        Messenger::IMsgrObject2Ptr oMsgrObj = NULL;
        Messenger::IMsgrUsersPtr oUsers = NULL;
        Messenger::IMsgrUserPtr oUser = NULL;
        _variant_t v;		
    
        // Create an instance of Instant Messenger.
        oMessenger.CreateInstance("Messenger.MessengerApp");
        oMsgrObj.CreateInstance("Messenger.MsgrObject");
        // Get a list of contacts.
        oUsers = oMsgrObj->GetList(Messenger::MLIST_CONTACT);
        // Loop through all contacts.
        for (long l=0; l<(oUsers->GetCount()-1); l++)
        {
            // Get a specific contact.
            oUser = oUsers->Item(l);
            // Check to see if the contact is the correct one.
    	if (wcscmp(_wcslwr(oUser->GetFriendlyName()),_wcslwr(Text)) == 0)
            {
                switch (VerbID) {
                  case 1:
                    // The user wants to display the Instant Message
                    // box to send the contact a message.
                    v = oUser.GetInterfacePtr();					
                    oMessenger->LaunchIMUI(v);
                    break;
                  case 2:
                    // Shell the "mailto" protocol to start the
                    // user's mail program and create a new message.
                    _bstr_t bstrTemp = "mailto:";
                    bstrTemp += oUser->GetEmailAddress();				                ShellExecute(0,"open",bstrTemp,NULL,NULL,1);
                    break;
                }
            }
        }
        return S_OK;
    }
  11. Stdafx.h 파일을 열고 다음 행을 #include <atlcom.h> 다음에 추가합니다.
    #import "C:\Program Files\Messenger\msmsgs.exe"
    참고 : Msmsgs.exe 파일 경로를 Instant Messenger 설치 위치로 변경합니다. Instant Messenger의 기본 위치는 C:\Program Files\Messenger입니다.
  12. F7 키를 눌러 DLL을 만듭니다.




스마트 태그 DLL을 등록하는 단계



스마트 태그 DLL을 사용하려면 시스템에 등록되어 있어야 합니다. DLL 이름으로 Regsvr32.exe를 호출하거나 프로젝트를 컴파일할 때 일반 COM 등록이 수행됩니다. 일반 COM 등록 부분이 아닌 추가 레지스트리 항목을 만들어야 Office 응용 프로그램에서 DLL을 스마트 태그 DLL로 구분할 수 있습니다. 이렇게 하려면 다음과 같이 하십시오.
  1. 명령줄에서 Regedit.exe를 시작합니다.
  2. HKEY_CURRENT_USER\Software\Microsoft\Office\Common\Smart Tag\Actions 에서 MessengerSmartTag.Action이라는 새로운 하위 키를 추가합니다.
  3. HKEY_CURRENT_USER\Software\Microsoft\Office\Common\Smart Tag\Recognize 에서 MessengerSmartTag.Recognize라는 새로운 하위 키를 추가합니다.
  4. 레지스트리 편집기를 닫습니다.




스마트 태그 DLL을 테스트하는 단계



스마트 태그는 매크로와 동일한 보안 모델을 사용합니다. 응용 프로그램의 보안 설정이 높음으로 설정된 경우 DLL이 디지털로 서명되지 않았다면(VBA 매크로와 마찬가지로) 스마트 태그 DLL은 로드되지 않습니다. 디지털 서명에 대한 자세한 내용은 "참조" 절을 참조하십시오.

사용자 지정 스마트 태그 인식자/작업 DLL을 Word에서 테스트하려면 다음 절차를 수행합니다.
  1. Instant Messenger를 시작하고 로그온합니다.

    참고 : 예제 스마트 태그의 경우 Instant Messenger에 로그온해야 합니다. Instant Messenger에 로그온하지 않으면 사용자 지정 DLL은 로드되지만 연락처를 인식하지 못합니다.
  2. Start Word 2002를 시작하고 도구 메뉴에서 매크로 를 가리킨 다음 보안 을 누릅니다. 매크로 보안을 보통 으로 설정하고 확인 을 누릅니다. 매크로 보안 설정이 이전에 높음 으로 설정된 경우 Word를 다시 시작합니다.
  3. 새 문서에 알기 쉬운 연락처 이름(예: 김 수범)을 입력하고 Enter 키를 누릅니다. 알기 쉬운 이름 아래에 희미한 선이 나타나면 그 이름이 스마트 태그로 인식된 것입니다. 마우스를 알기 쉬운 이름으로 이동하면 스마트 태그 동작 단추가 나타납니다.
  4. 스마트 태그 동작 을 누르고 드롭다운 메뉴에서 사용자 지정 동작 항목 중 하나를 선택합니다. 전자 메일이나 인스턴트 메시지를 새 문서에서 연락처로 보낼 수 있습니다.
비슷한 단계를 사용하여 Excel 2002에서 스마트 태그 DLL을 테스트할 수 있습니다.





문제 해결



사용자 지정 스마트 태그가 작동하는 데 문제가 있으면 먼저 사용자 지정 스마트 태그 DLL이 로드 중인지 확인합니다. Word나 Excel에서 도구 메뉴에 있는 자동 고침 옵션 을 누르고 스마트 태그 탭을 누른 다음 스마트 태그 데이터에 레이블 표시 가 선택되어 있는지, 스마트 태그 DLL이 나열되어 있고 선택되어 있는지 확인합니다. 스마트 태그가 나열되어 있지 않으면 제대로 등록되지 않은 것입니다.

사용자 지정 인식자나 동작 클래스 실행이 문제의 원인이면 Visual C++ DLL에서처럼 스마트 태그 DLL을 디버그할 수 있습니다. Recognizer 클래스에 대한 중단점을 생성자에 설정합니다. F5 키를 눌러 응용 프로그램을 디버그하면 디버그 세션에 대한 실행 파일을 묻는 대화 상자가 나타납니다. Winword.exe 또는 Excel.exe 를 선택합니다. Excel이나 Word에서 스마트 태그를 시작하고 로드하면 코드는 중단점에서 중단되므로 디버깅할 코드를 단계별로 실행할 수 있습니다.



참조


이러한 인터페이스에 대한 설명서는 인터페이스를 정의하는 필요한 형식 라이브러리와 함께 Smart Tag Software Development Kit(SDK)에 제공됩니다. 그렇지 않은 경우에는 예제 스마트 태그를 만드는 단계를 진행하기 전에 Smart Tag SDK를 설치해야 합니다. Microsoft Office XP Developer(MOD) CD-ROM에서 Smart Tag SDK를 구하거나 아래 Microsoft Developer Network(MSDN) 웹 사이트에서 다운로드할 수 있습니다.
Smart Tag Software Development Kit
http://www.microsoft.com/downloads/details.aspx?familyid=3d8edcee-0246-43ef-a70f-94c0899f1e8f&displaylang=en



사용자 지정 스마트 태그 인식자/동작 DLL 만들기에 대한 자세한 내용은 Smart Tag SDK에 포함된 Smart Tag Development Help 파일을 참조하십시오.

디지털 서명에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
247257 INFO: .cab Æ??? ¼­¸? ´?°?
ProgID 대신 CLSID를 사용하여 스마트 태그 DLL을 등록하는 방법에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
294422 BUG: Status Flag Is Not Updated When You Enable or Disable Smart Tags




속성

기술 자료: 292596 - 마지막 검토: 2014년 2월 24일 월요일 - 수정: 3.1
본 문서의 정보는 다음의 제품에 적용됩니다.
  • Microsoft Excel 2002 Standard Edition
  • Microsoft Word 2002 Standard Edition
  • Microsoft Visual C++ 6.0 Professional Edition
키워드:?
kbnosurvey kbarchive kbhowtomaster kbatl kbword kbexcel kbgrpdso KB292596

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com