【轉載】ATL問題集

原文:http://blog.csdn.net/fengrx/article/details/4171629git

 

這些問題是之前在csdn當版主是一些朋友整理的,今天找到了,貼到這裏來!web

#1 如何使用控件不能改變大小?redis

答:有時咱們須要建立不可改變大小的控件,像那種在運行時沒有界面的控件(例:時間控件,SysInfo 等),想作到這種功能的話,請把如下代碼加入到控件類的構造函數:算法

m_bAutoSize = TRUE;

SIZEL size = {24, 24};
AtlPixelToHiMetric(&size, &m_sizeExtent);
m_sizeNatural = m_sizeExtent;windows

#2.如何在運行時顯示屬性頁?數組

答:在CComControlBase::DoVerbProperties() 中會自動調用ISpecifyPropertyPages::GetPages()::OleCreatePropertyFrame() 且建立與顯示OLE屬性頁,只要從你的控件中簡單調用DoVerbProperties()顯示,如何下代碼:瀏覽器

HRESULT STDMETHODCALLTYPE PopMeUp(void)
{
return DoVerbProperties(NULL, ::GetActiveWindow() );
}安全

#3.如何在運行時新增長屬性頁?服務器

答:覆蓋ISpecifyPropertyPagesImpl::GetPages()來增長你的新屬性頁,或刪除它們,改變它們等到。如下代碼演示在已存在的屬性表中加入新的屬性頁:網絡

HRESULT STDMETHODCALLTYPE GetPages(CAUUID *pPages)
{
if(SUCCEEDED(ISpecifyPropertyPages_GetPages(pPages,NULL))
{
pPages->cElems += 1;
pPages->pElems = 
(GUID *)::CoTaskMemAlloc(pPages->cElems * sizeof(CLSID));
pPages->pElems[pPages->cElems - 1] = CLSID_General;
}
else
return E_FAIL;
}

#4 如何註冊控件?

這是一個很常見的問題,最簡單用Winodws自帶的Regsvr32或其它工具等,其原理是利用控件的RegsiterServer函數與UnregsiterServer來實現註冊與取消註冊,如下是代碼實現註冊:

DWORD RegisterServer( char* szPath )
{
   HINSTANCE hInstance = ::LoadLibrary( szPath );
   if ( 0 == hInstance )
   { 
      return ::GetLastError();
   } 
   typedef void (FAR PASCAL *REGSERVER)(void); 
   REGSERVER RegServer = (REGSERVER) ::GetProcAddress( hInstance, _T( "DllRegisterServer" ));
   if ( 0 == RegServer )
   { 
      ::FreeLibrary( hInstance );
      return ::GetLastError();
   } 
   RegServer(); 
   ::FreeLibrary( hInstance ); 
}

#5 我如何使用手工來控制大小?

答:你只要重載IOleObject接口的SetExtent方法.

// NoteCtl.h : Declaration of the CNoteCtl
...
class ATL_NO_VTABLE CNoteCtl : 
...
   STDMETHOD(SetExtent)(DWORD dwDrawAspect, SIZEL *psizel)
   {
      ATLTRACE(_T("SetExtent sizing control to 1000x1000 "));
      psizel->cx = psizel->cy = 1000;
      return IOleObjectImpl<CNoteCtl>::SetExtent(dwDrawAspect, psizel);
   }
...
};

#6 我如何從新設置控件的大小?

void CMyCtrl::SetNewSize (int cx, int cy)
{
   SIZEL szlPixels, szlMetric;
   szlPixels.cx = cx;
   szlPixels.cy = cy;

   AtlPixelToHiMetric(&szlPixels, &szlMetric);
   // IOleObjectImpl
   SetExtent(DVASPECT_CONTENT, &szlMetric);

   // update control sizing...
   m_rcPos.right= m_rcPos.left + cx;
   m_rcPos.bottom= m_rcPos.top + cy;
   if (m_spInPlaceSite != NULL) {
      // needed for IE to accept the resizing
      m_spInPlaceSite->OnPosRectChange(&m_rcPos);
   }
   SetFocus();

#7 如何取得當前容器是在設計狀態?

答:ATL提供了CComControlBase::GetAmbientUserMode()來取得其狀態.

BOOL IsUserMode()
{
BOOL bUserMode = TRUE;
HRESULT hRet = GetAmbientUserMode(bUserMode);

if (FAILED(hRet) || bUserMode)
{
return TRUE; 
}
return FALSE;
}

#8 如何使某些只能在運行時修改?

答:COleControl提供了兩個方法來輔助實現:AmbientUserModeGetNotSupported,AmbientUserMode()來取得當前容器的狀態,是在運行時仍是設計時;GetNotSupported()能產生CTL_E_GETNOTSUPPORTED自動化異常.

HRESULT CNoteCtl::get_RuntimeOnly( long* pTest )
{
   BOOL bUserMode;
   GetAmbientUserMode( bUserMode );
   if (! bUserMode ) 
      return CTL_E_GETNOTSUPPORTED;

   *pTest = 100;
   return S_OK;
}

#9 如何作一個簡單的控件容器?

MFC控件嚮導支持簡單框架控件,ATL 2.1不支持,如下代碼演示在ATL 3.0(VC6環境)中實現簡單的ISimpleFrameSite的容器框架.

1.定義兩個宏(主要是爲了方便)

#define RELEASE_OBJECT( ptr )if (ptr) { IUnknown *pUnk = (ptr); (ptr) = NULL; pUnk->Release(); }

#define QUICK_RELEASE(ptr) if (ptr) ((IUnknown *)ptr)->Release();

2.在控件類中加入成員變量:

ISimpleFrameSite* m_pSimpleFrameSite;

3.在控件類的構造函數中加入:

m_pSimpleFrameSite = NULL;

4.在控件類的析構函數中加入:

QUICK_RELEASE(m_pSimpleFrameSite);

5.覆蓋IOleObject::SetClientSite:

STDMETHOD(SetClientSite)(IOleClientSite *pClientSite)

{

   HRESULT hr = IOleObjectImpl<你的控件類>::SetClientSite(pClientSite);

 

   RELEASE_OBJECT(m_pSimpleFrameSite);

   if( pClientSite != NULL )

      pClientSite->QueryInterface( IID_ISimpleFrameSite,

                                   (void **)&m_pSimpleFrameSite);

 

   return hr;

}

6.在控件類中加入成員變量:

WNDPROC m_fnOldWindowProc;

7.覆蓋Create函數:

HWND Create( HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL,

             DWORD dwStyle = WS_CHILD | WS_VISIBLE, DWORD dwExStyle = 0, UINT nID = 0 )

{

   HWND hWnd = CWindowImpl<你的控件類>::Create( hWndParent, rcPos,

                                                szWindowName, dwStyle, dwExStyle, nID);

   if (hWnd)

   {

      ::SetProp(hWnd, "ABC", static_cast<HANDLE> ((你的控件類*) this));

      m_fnOldWindowProc = (WNDPROC) ::SetWindowLong( hWnd, GWL_WNDPROC, (LONG) SimpleFrameWindowProc);

   }

   return hWnd;

}

8.在你的控件類中加入定義:

static LRESULT CALLBACK SimpleFrameWindowProc( HWND hWnd, UINT uMsg,

                                               WPARAM wParam, LPARAM lParam );

9.加入實理代碼:

LRESULT CALLBACK 你的控件類::SimpleFrameWindowProc( HWND hWnd, UINT uMsg,

                                                    WPARAM wParam, LPARAM lParam )

{

   你的控件類* pThis = static_cast<你的控件類*> (::GetProp(hWnd, "ABC"));

   WNDPROC fnOldWindowProc = pThis->m_fnOldWindowProc;

 

   LRESULT lResult;

   BOOL bProcess = TRUE;

   DWORD dwCookie;

   HRESULT hr = E_FAIL;

 

   if(pThis->m_pSimpleFrameSite)

   {

      hr = pThis->m_pSimpleFrameSite->PreMessageFilter(hWnd, uMsg, wParam,

                                             lParam, &lResult, &dwCookie);

      bProcess = (hr != S_FALSE);

   }

 

   if (bProcess)

      lResult = fnOldWindowProc(hWnd, uMsg, wParam, lParam);

 

   if(pThis->m_pSimpleFrameSite && bProcess)

   {

      pThis->m_pSimpleFrameSite->PostMessageFilter( hWnd, uMsg, wParam, lParam,

                                                    &lResult, dwCookie);

   }

 

   return lResult;

}

10.在.RGS文件中的MiscStatus中新增長OR 0x10000

'MiscStatus' = s '0'

{

   '1' = s '131473'

}

to:

'MiscStatus' = s '0'

{

   '1' = s '197009'

}

#10 如何在ATL控件中使用Dialog資源?

答:這兒是Microsoft的Mark Davis的回答:

1.使用ATL對象嚮導新增長對話框資源(例如:CMyDialog)。
2.編輯Dialog。
3.在你的控件類中加入內部成員變量(例如:CMyDialog m_dlg)。
4.在你的控件中映射消息WM_CREATE,在消息處理函數裏建立Dialog(例如:m_dlg.Create(m_hWnd))

有時你的處理一些標準的Windows窗口的問題,像WM_SIZE等,根據你的狀況來做相應的處理。

#11 如何在個人控件加入AboutBox?

答:1.在接口中加入新的方法,並在接口文件(.idl)中改變dispid爲DISPID_ABOUTBOX。
2.產生Dialog資源,並設置ID爲IDD_ABOUTBOX。
3.在你的控件中加入如下代碼: 
class CAboutDlg : public CDialogImpl<CAboutDlg>
{
public:
   enum { IDD = IDD_ABOUTBOX };
   BEGIN_MSG_MAP(CAboutDlg)
      COMMAND_ID_HANDLER(IDOK, OnOK)
   END_MSG_MAP()

   HRESULT OnOK(WORD, WORD, HWND, BOOL&)
   {
      EndDialog(0);
      return 0;
   }
};

4.在你當才加的新方法中加入實現代碼,例如:
CAboutDlg dlg;
dlg.DoModal();

#12 如何處理控件的滾動條?

在你的Active X控件中加入滾動條須要在你的控件類的構造函數中把窗口m_bWindowOnly標誌設置爲TRUE,你也須要映射與處理消息WM_CREATE,並在處理函數中在窗口類型中加入WS_HSCROLL與WS_VSCROLL類型,如如下代碼:

LRESULT OnCreate(UINT nMsg, WPARAM wParam,
    LPARAM lParam, BOOL& bHandled)
{
DWORD dwStyle = GetWindowLong(GWL_STYLE);

dwStyle |= WS_VSCROLL | WS_HSCROLL;

SetWindowLong(GWL_STYLE, dwStyle);
return 0L;
}

映射與處理消息WM_HSCROLL與WM_VSCROLL,並並覆蓋TranslateAccelerator()來加入鍵盤支持:

STDMETHOD(TranslateAccelerator)(MSG *pMsg)
{
switch(pMsg->wParam)
{
case VK_UP:
{
::SendMessage(m_hWnd, WM_VSCROLL,
    SB_LINEUP, MAKELONG(0,m_hWnd));
break;
}
case VK_DOWN:
{
::SendMessage(m_hWnd, WM_VSCROLL,
    SB_LINEDOWN, MAKELONG(0,m_hWnd));
break;
}
//以上面類似:
// case VK_LEFT:
// case VK_RIGHT:
// case VK_PRIOR: 
// case VK_NEXT:

}

return S_FALSE;

#13 如何使個人控件對IE來講是安全的?

要使控件對IE來講是安全的話,則必需實現IObjectSafety接口,ATL提供了IObjectSafetyImpl包裝類,如下代碼是演示這個功能,加精是新增長的:

class ATL_NO_VTABLE CNoteCtl :
   public CComObjectRootEx<CComSingleThreadModel>,
   ...
   // Derive from IObjectSafety
public IObjectSafety 
{
...
BEGIN_COM_MAP(CNoteCtl)
   COM_INTERFACE_ENTRY(INoteCtl)
   COM_INTERFACE_ENTRY(IDispatch)
   ...
   // Add it to our interface map
COM_INTERFACE_ENTRY(IObjectSafety) 
END_COM_MAP()
   ...
   // IObjectSafety implementation
   STDMETHODIMP GetInterfaceSafetyOptions( REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions )

   {
      ATLTRACE(_T("CNoteCtl::GetInterfaceSafetyOptions() "));
   
      *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER |
                             INTERFACESAFE_FOR_UNTRUSTED_DATA;
      *pdwEnabledOptions = *pdwSupportedOptions;
      return S_OK;
   }

   STDMETHODIMP SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
   {
      ATLTRACE(_T("CNoteCtl::SetInterfaceSafetyOptions "));
      return S_OK;
   }...
};

#14 如何在控件中使用字體?

在ATL 2.x開始支持內置字體屬性,首先,處理這個屬性不像MFC那麼簡單;第二,你須要在你的控件的IDL文件中加入字體屬性的聲明(其實在VC6的ATL嚮導中支持這些屬性了,你在嚮導中選上的話,嚮導自動會在idl文件中加入相關聲明)

ATL並無徹底實現內置字體屬性,它提供了內部成員變量指向IFontDisp接口,但是你仍然須要進行OLE字體的初始化,如下代碼是演示:

在你的控件類的構造函數中加入如下代碼:

CMyCtl(){   static FONTDESC _fontDesc =     { sizeof(FONTDESC), OLESTR("MS Sans Serif"),       FONTSIZE( 12 ), FW_BOLD,        ANSI_CHARSET, FALSE, FALSE, FALSE };   OleCreateFontIndirect( &_fontDesc,IID_IFontDisp,(void **)&m_pFont );}

在你須要使用的地方使用如下代碼,通常是在控件的OnDraw方法中,以下:

//取得字體CComQIPtr<IFont, &IID_IFont> pFont( m_pFont使用它...   if ( hOldFont )      SelectObject( hdc, hOldFont );} );if ( pFont ){   HFONT hOldFont = 0;   HFONT hFont;   pFont->get_hFont( &hFont );   hOldFont = (HFONT) SelectObject( hdc, hFont );   //

通常在VC6的ATL嚮導中選擇了Font字體屬性的話,嚮導會在IDL文件中自動產生如下代碼,沒有的話手工加入如下聲明(加粗部分):

#include <olectl.h>import "oaidl.idl";[       uuid(E63A22F1-9BD3-11D0-A6D7-0000837E3100),        version(1.0), helpstring("NoteIt 1.0 Type Library")]library NOTEITLib{   importlib("stdole32.tlb");   importlib("stdole2.tlb");   // Interface is now inside the library block   [      object,      uuid(E63A2306-9BD3-11D0-A6D7-0000837E3100),      dual,      helpstring("INoteCtl Interface"),      pointer_default(unique)   ]   interface INoteCtl : IDispatch   {      ...      [propputref, id(DISPID_FONT)]      HRESULT Font([in]IFontDisp* pFont);      [propput, id(DISPID_FONT)]      HRESULT Font([in]IFontDisp* pFont);      [propget, id(DISPID_FONT)]      HRESULT Font([out, retval]IFontDisp** ppFont);      ...   };...}

#15 COM/ATL中如何處理錯誤?

基於Windows的組件都有支持ISupportErrorInof接口,它容許將組件的錯誤信息返回給客戶端,在VC5之後提供了本地的支持,以下:

_com_error( HRESULT hr, IErrorInfo* perrinfo = NULL ) throw( );

_com_error( const _com_error& that ) throw( );

這個函數檢查IErrorInfo接口指針是否存在,若是存在將拋出_com_error異常對象,你只要捕獲這個_com_error異常對象就要以了,如下是示例代碼:

STDMETHODIMP CMessageHandler::NewMessage(BSTR inMessage, BSTR inTo,
                                         BSTR inFrom, BSTR inReply)
{
    HRESULT hr = S_OK;

    try
 {

    ......

    if(FAILED(hr))
        _com_error(hr);
    }
    catch (_com_error& e) {
        hr = Error((BSTR)e.Description(), e.HelpContext(), e.HelpFile(),e.GUID(), e.Error());
        ATLTRACE("com error: %d - %s ", e.Error(), (const char*)e.Description());
    }
    return hr;
}

至於返回錯誤信息到客戶端,請參閱個人《COM的錯誤處理》(也在文檔中心)。

#16 如何自定義控件的Verbs?

Microsoft標準文檔定義了OLE對象從容器中響應消息,在一個對象容器或客戶端連接到對象,一般是調動IOleObject::DoVerb()來響應用戶或容器的消息,你能夠經過雙擊對象或點擊鼠標右鍵的上下文菜單來提供的選擇來操做,容器對象裝入上下文菜單是經過調用IOleObject::EnumVerbs().

典型的服務對象或控件是在IOleObject::EnumVerbs()的實現中調用OleRegEnumVerbs() ,ATL默認實現了這些功能,但你必須按照如下步驟:

1.首先添加菜單項到.RGS文件中,verb關鍵字存儲在註冊,以下:

HKEY_LOCAL_MACHINESOFTWAREClassesCLSIDVerb 
      1 = <verb1>
      2 = <verb2>
      3 =

如下是verb的格式:

Verb_Number = <Verb_String, Menu_Flag, Verb_Flag>

Verb_Number是個枚舉類型,Verb_String是有效的字符串,像"屬性",Menu_Flag描述如何調用::AppendMenu,Verb_Flag是OLEVERBATTRIB枚舉類型的值之一,以下:

OLEVERBATTRIB_NEVERDIRTIES       = 1,
OLEVERBATTRIB_ONCONTAINERMENU    = 2

因此請修改你的.RGS文件,以下:

   NoRemove CLSID
   {
      ForceRemove {E14A8DEA-8C72-11D1-891C-00C04FA3FB11} = s 'X Class'
      {
         ProgID = s 'X.X.1'
         VersionIndependentProgID = s 'X.X'
         ForceRemove 'Programmable'
...
         'verb'
         {
            '1' = s '&Play,0,2'
'2' = s '&Transpose,0,2'
'3' = s '&Detune,0,2'
'4' = s '&Properties,0,2'
         }
...
      }
   } 

當容器檢測到做過在對象上的verb操做將調用IOleObject::DoVerb(),在ATL,你須要覆蓋IOleObjectImpl::DoVerb(),以下:

STDMETHOD(DoVerb)(LONG iVerb,
LPMSG lpmsg, 
IOleClientSite *pActiveSite,
LONG lindex,   
HWND hwndParent, 
LPCRECT lprcPosRect)
{
if (iVerb == 1)//The verb number mentioned in the .rgs file
   {
       //Do whatever you want
   }
else if(iVerb == 2)
{
}
?
?

return IOleObjectImpl<ClassName>::DoVerb(iVerb, lpmsg,
    pActiveSite, lindex, hwndParent, lprcPosRect);
}

#17 ATL裏設置默認屬性、默認方法?

對於屬性只要在.IDL文件中將其ID設爲0就好了。如:

[propget, id(0), helpstring("property test")] HRESULT test([out, retval] short *pVal);


同理對於方法也生效。

#18 如何使某個參數可選擇?

HRESULT MyFunc([in]BSTR szName,[in, optional] VARIANT Param1, [out, optional] VARIANT Param2)

你在MyFunc程序中得檢查Param1.vt是否爲VT_EMPTY,若是是,用戶未使用該參數。

#19 如何使用自定義結構和枚舉類型?

在你的IDL文件中加入以下類似的代碼:

 

typedef struct _Point2D

{

double x;

double y;

} Point2D;

HRESULT GetPos([out,retval]Point2D* pvar);

typedef enum tagFontAlign
{
[helpstring("Left")]Left=0,
[helpstring("Center")]Center=1,
[helpstring("Right")]Right=2,
}FontAlign;
[propget, id(2), helpstring("對齊方式")] HRESULT Align([out, retval] FontAlign *pVal);
[propput, id(2), helpstring("對齊方式")] HRESULT Align([in] FontAlign newVal);
在接下來的接口定義中添加屬性Align時,屬性的數據類型就填FontAlign,其它操做照常。編譯完之後,你就應該在VB Project中的Object Browser中看到有這麼一個枚舉類型。在控件屬性中選中Align時,就會有個Combo Box讓你選擇FontAlign中的一個值。 

加上一句,若是用的是atl7

那就這麼用:在.h中也能夠用,只要在接口聲明的.h中包含它的.h便可!

[export]

enum wwx

{

a=0,

b=1,

c=3

};其餘用法同樣

[export]

struct Point2D

{

double x;

double y;

};

#20 OLE_COLORCOLORREF的有區別嗎?

OLE_COLOR與COLORREF之間是有必定區別的:OLE_COLOR和COLORREF都是DWORD類型,但對於COLORREF來講,它的最高一個字節永遠是0x00。即若是是紅色,對於COLORREF來講是0x000000FF。而OLE_COLOR的最高一個字節有兩種狀況:0x80(也就是10000000,最高位是1)或0x00(也就是00000000,最高位是0)。當OLE_COLOR的最高位是0時,它與COLORREF是相同的,最後三個字節表明RGB,能夠相互賦值。例如紅色用OLE_COLOR來表示一樣是0x000000FF。但當OLE_COLOR的最高位是1時,它的中間兩個字節必定都是0x00,最後一個字節表示的是系統顏色索引值。例如系統定義菜單的顏色索引值是4,因此用OLE_COLOR來表示就是0x80000004。在VB中,若是你選中一個FORM,在它的屬性頁中你能夠看到它的BackColor屬性,你點擊下拉框,就能夠選擇是使用調色板色仍是系統色,調色板色就是對應了OLE_COLOR的高位爲0的狀況,系統色對應的是OLE_COLOR高位爲1的狀況。你試一下就知道是怎麼回事了,詳細請參看:MSDN/Platform SDK/Component Services/COM/Controls and Property Pages/Functions/OleTranslateColor的Remarks。

OLE_COLOR與COLORREF的轉達換處理:在MFC中可有OLEControl::TranslateColor()來轉達換,在ATL或其它地方可調用API:OleTranslateColor()來進行從OLE_COLOR到COLORREF的轉換。返過來可用以下方法:OLE_COLOR ocConverted = (OLE_COLOR)clrBack;

一樣,VARIANT_BOOL和BOOL之間也有區別:BOOL爲long,在BOOL中,TURE爲1,FALSE爲0。VAIRNAT_BOOL爲short,在VARIANT_BOOL中,VARIANT_TRUE爲-1(0xFFFF),VARIANT_FALSE爲0(0x0000)。而且VARIANT_BOOL是和VB中的Boolean相同的,就像BSTR和String的關係同樣。因此,在自動化組件及控件中應該使用VARIANT_BOOL。

#21 如何讓個人控件輸出數組?

參閱以下代碼:

void CMyControl::GetArray( VARIANT FAR* pVariant ){    //商業代碼   int nCount = GetCount();

   //定義維數   SAFEARRAYBOUND saBound[1];   //定義數組指針對性   SAFEARRAY* pSA;   saBound[0].cElements = nCount;   saBound[0].lLbound = 0;   //建立數組   pSA = SafeArrayCreate( VT_BSTR, 1, saBound );   for( long i = 0; i < nCount; i++ )   {      BSTR bstr;      //商業代碼      bstr = GetItem( i ).AllocSysString(); //給數組賦值      SafeArrayPutElement( pSA, &i, bstr );      ::SysFreeString( bstr );   }   // 初始化傳遞的參數   VariantInit( pVariant );   //設置返值的類型爲數組   pVariant->vt = VT_ARRAY | VT_BSTR;   pVariant->parray = pSA;}

Visual Basic 代碼:

    Dim t As Variant    Dim i as Integer    MyControl1.GetArray t    For i = 0 To MyControl1.Count - 1        ListBox.AddItem t( i )    Next i

#22 如何取得控件的HWND?

     HWND CMyOcx::GetApplicationWindow()
      {
         HWND  hwnd = NULL;
         HRESULT hr;
     
         //*****這段代碼在VC++ v4.1工做
         if (m_pInPlaceSite != NULL)
             {
             m_pInPlaceSite->GetWindow(&hwnd);
             return hwnd;
             }
     
         //****** 這段代碼在Visual Basic工做
         LPOLECLIENTSITE pOleClientSite = GetClientSite();
     
         if ( pOleClientSite )
          {
             IOleWindow* pOleWindow;
             hr = pOleClientSite->QueryInterface( IID_IOleWindow, (LPVOID*) 
                    &pOleWindow );
     
             if ( pOleWindow )
              {
                 pOleWindow->GetWindow( &hwnd );
                 pOleWindow->Release();
                 return hwnd;
              }
          }
     
         return NULL;
     }

#23 爲何AmbientUserMode老是返回TRUE?

答:若是你在控件類的構造函數,析構函數,OnSetClientSite方法中使用AmbientUserMode()會老是返回TRUE,由於控件還未設置ambient IDispatch鏈接點到容器,下面演示在OnSetClientSite()中取得其值:

void CYourCtrl::OnSetClientSite()
{
  if ( m_ambientDispDriver.m_lpDispatch && AmbientUserMode() )
    RecreateControlWindow();//商業代碼
}

m_ambientDispDriver變量是用於維護COleControlambient的自動化接口,只有它m_lpDispatch有效時纔會返回這個屬性值。

#24 如何在控件中控制鍵盤?

用ATL開發控件常常須要在一個活動的Form(VB的表單)中處理鍵盤,若是ActiveX控件容器中包含了其它子窗口或窗口控制須要對鍵盤進行控制的話,那麼你須要在控件中實現幾個方法,具備UI界面的控件老是會調用IOleInPlaceActiveObject::TranslateAccelerator() 與IOleControl::GetControlInfo(),你可能須要覆蓋IOleControl::OnMnemonics()與正確處理Windows的鍵盤消息,而無論是個容器仍是在用戶模式。

下面演示在ATL開發Active X控件中在子窗口中處理鍵盤消息。

1.UI激活:當控件初激活時才能收到鍵盤消息,如下代碼演示如何在ATL控件中處理WM_MOUSEACTIVEATE消息來激活UI,它使用了IsUserMode()方法中使用CComControlBase::InPlaceActivate(OLEIVERB_UIACTIVATE)來完成UI激活,這後控件就能夠接收鍵盤消息了。

LRESULT OnMouseActivate(UINT uMsg,
    WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if(IsUserMode())
{
InPlaceActivate(OLEIVERB_UIACTIVATE); 
}
return 0;
}

2.設置子窗口焦點:

LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled)
{
CComControl<CJazzControl>::OnSetFocus (uMsg,
    wParam, lParam, bHandled);

if (IsUserMode())
{
InPlaceActivate (OLEIVERB_UIACTIVATE); 
m_ChildControl.SetFocus();
}
return 0;
}

3.實現IOleInPlaceActiveObject::TranslateAccelerator().ATL提供了IOleInPlaceActiveObject接口的包裝類IOleInPlaceActiveObjectImpl,ATL 2.1默認實現IOleInPlaceActiveObjectImpl::TranslateAccelerator()返回E_NOTIMPL,你須要覆蓋它:

STDMETHOD(TranslateAccelerator)(MSG *pMsg)
{
if (
((pMsg->message >= WM_KEYFIRST) &&
    (pMsg->message <= WM_KEYLAST))
&&
  ((pMsg->wParam == VK_TAB) ||
    (pMsg->wParam == VK_RETURN))
)
{
CComQIPtr<IOleControlSite,&IID_IOleControlSite> 
spCtrlSite(m_spClientSite);
if(spCtrlSite)
{
return spCtrlSite->TranslateAccelerator(pMsg, 0);
}
}
return S_FALSE;
}

上述的代碼是在子窗口的編輯框中處理TAB與ENTER鍵,若是你須要處理UP ARROW, DOWN ARROW, PAGE UP, and PAGE DOWN,可以下示例:

if((pMsg->wParam == VK_UP) ||
    (pMsg->wParam == VK_DOWN)||
   (pMsg->wParam == VK_LEFT) ||
       (pMsg->wParam == VK_RIGHT))
{
::IsDialogMessage(m_hWnd, pMsg);
return S_OK; 
}

若是Active X控件有滾動條,你須要處理VK_UP與VK_DOWN,以下示例:

if (pMsg->wParam == VK_UP)
{
::SendMessage(m_hWnd,WM_VSCROLL,
   SB_LINEUP,MAKELONG(0,m_hWnd));
return S_FALSE;
}

默認按鈕的處理:當用戶按下ENTER,你應該容許焦點轉移到默認的按鈕上(若是一個按鈕設置爲「默認」),那麼你須要實現IOleControl::GetControlInfo()來接受ENTER與ESC鍵,ATL默認實現IOleControlImpl::GetControlInfo()返回E_NOTIMPL,你須要覆蓋它:

HRESULT STDMETHODCALLTYPE GetControlInfo(CONTROLINFO *pCI)
{
if(!pCI)
{
return E_POINTER;
}
pCI->hAccel = NULL; //load your accelerators here, if any   
pCI->cAccel = 0;    
pCI->dwFlags = 0;

return S_OK;
}

#25 如何持續化參數屬性?

在正常狀態下支持二進制與文本的持續化,控件分別須要實現IPersistStreamInit與IPersistPropertyBag接口,ATL提供了該接口的包裝類IPersistStreamInitImpl與IPersistPropertyBagImpl,在裝入與存儲屬性中,這兩個類分別調用了CComControlBase::IPersistStreamInit_Load()/Save()CComControlBase::IPersistPropertyBag_Load()/Save() ,且調用CComDispatchDriver::GetProperty()在這裏面又調用了invoke()來指定特殊的屬性值,而後CComDispatchDriver::GetProperty()只實現支持單個屬性值,籤於此點,ATL 2.1的屬性持續化機制不支持索引屬性。

要突破這個限制,得在你的控件中覆蓋持續化路徑,並依照標準來實現文本與二進制的持續化,做爲替代,你的屬性應象這樣定義:

[propget, id(4), helpstring("Indexed Property")] HRESULT ParamProp(
[in] short nIndex, [out, retval] short *pVal);

[propput, id(4), helpstring("Indexed Property ")] HRESULT ParamProp(
[in] short nIndex, [in] short newVal);

爲了支持IPersistStreamInit你須要覆蓋CComControlBase::IPersistStreamInit_Save():

HRESULT IPersistStreamInit_Save(LPSTREAM pStm,
BOOL  fClearDirty , 
ATL_PROPMAP_ENTRY* pMap
)
{
if(!pStm)
{
return E_POINTER;
}

for(UINT nIndex = 0; nIndex < 12; nIndex++)
{
if(FAILED(pStm->Write(&(m_nColor[nIndex]),
    sizeof(m_nColor[nIndex]), NULL))
{
return E_UNEXPECTED;
}
}


//調用默認的基類來實現存儲單屬性值PROP_MAP

return CComControlBase::IPersistStreamInit_Save(pStm,  fClearDirty, pMap);

}

若是你要實現IPersistPropertyBag接口,你得覆蓋了Load()Save()方法.

#26 ATL發行版本中出錯信息:「unresolved external symbol _main」

答:這是VC6的一個BUG,因爲VC6在ATL使用_ATL_MIN_CRT_宏,該宏會使CRT啓動代碼無效,去掉該宏就能夠了,以下作法:Project->Setting->C/C++ 的Category中選擇Preprocessor的Preprocessor definitions:中去掉_ATL_MIN_CRT_。

#27 如何在ATL中取得windowsless窗口的HWND

答:windowsless 就是沒有窗口。你的ATL控件沒有窗口, m_hWnd不是NULL能是什麼。至於Ondraw獲得的 hdc 實際是父窗口的hdc。huhu 你注意看 M$ 的form 系列控件(就是IE頁面中的那些textbox checkbox ....), 都是windwosless的。
if (m_bWndLess) 

HDC hDC; 
HWND hWnd; 
// Get the HDC from the client 
m_spInPlaceSite->GetDC(NULL, OLEDC_NODRAW, &hDC); 
// Get the HWND from the HDC 
hWnd = WindowFromDC(hDC); 
m_spInPlaceSite->ReleaseDC(hDC); 

注意:不要亂動那個hWnd由於這個東西不是你的。

#28 如何在客戶端中使用CoCreateInstanceEx()?

答:stdafx.h的最前面加入#define _WIN32_DCOM
 

#29 爲什麼在Visual C++ Compoents中找不到ATL proxy Generator組件?

答:這是VC5爲支持Connection Point的作法,VC6已整合到Wizard裏面。具體位置:選擇編譯你的項目,而後直接在你的類中擊鼠標右鍵選擇Implement Connection Point,後面的界面與VC5的如出一轍。

#30 ATL中如何使用IPicture接口顯示圖片?

一下描述一種最簡單的在 ALT 中使用 IPicture 來 顯示圖片的實例。控件的屬性頁能夠選擇圖片, 選好後控件的背景就變成該圖片
1.創建一個ALTproject,加入ALT對象選 controls full controls (也能夠選別的)NextStock propertiesPicture 加入 supported  //這樣,會爲控件生成一個picture屬性,以及一個預製的 picture 屬性對話框,方便選擇圖片。OK//m_pPicture 是一個 IPictureDisp.
//因爲M$的一個BUG 致使 build有三個warning 先不要管它,後面會有解決辦法
2.修改 HRESULT OnDraw(ATL_DRAWINFO& di)以下

HRESULT OnDraw(ATL_DRAWINFO& di)
{
RECT& rc = *(RECT*)di.prcBounds;
Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
/////////////////////////////////////draw  our picture
LPPICTURE pPict ;
DWORD dwAttr  ;
OLE_XSIZE_HIMETRIC cxSrc;
OLE_YSIZE_HIMETRIC cySrc;
if ((m_pPicture != NULL) &&SUCCEEDED(m_pPicture->QueryInterface(IID_IPicture, (LPVOID*)&pPict)))
{
pPict->get_Attributes(&dwAttr);
if(dwAttr==S_OK)
{
pPict->get_Width(&cxSrc);
pPict->get_Height(&cySrc);
pPict->Render(di.hdcDraw,rc.left, rc.top, rc.right, rc.bottom,0,0,cxSrc,cySrc,&rc);
}
}
///////////////////////////////////////finished draw
SetTextAlign(di.hdcDraw, TA_CENTER¦TA_BASELINE);
LPCTSTR pszText = _T("ATL 3.0 : catest");
TextOut(di.hdcDraw,
(rc.left + rc.right) / 2,
(rc.top + rc.bottom) / 2,
pszText,
lstrlen(pszText));

return S_OK;
}

#31 什麼是GUID

GUID用於標識軟件接口,它與COM(部件對象模型)中用於標識COM接口的標識符相同,它還用於OSF(開放軟件基金)的DCE(分佈式計算環境)中,標識RPC(遠程過程調用)目標。若是你想了解GUID如何生成以及爲何能在統計意義上惟一,請參考Kraig Brockschmidt的《Inside OLE, Second Edition (Microsoft Press, 1995)》第66頁,原始算法規範由OSF制定,相關部分見http://www.opengroup.org/onlinepubs/9629399/apdxa.htm

爲了在設備驅動程序中使用GUID,首先須要使用UUIDGEN或者GUIDGEN生成GUID,而後把結果放到頭文件中。GUIDGEN更易於使用,它容許選擇GUID的輸出格式,並把結果送到剪貼板。圖2-18顯示了GUIDGEN的運行窗口。你能夠把它的輸出粘貼到頭文件中:

// {CAF53C68-A94C-11D2-BB4A-00C04FA330A6}
DEFINE_GUID(<<name>>, 0xCAF53C68, 0xA94C, 0x11D2, 0xBB, 0x4A, 0x00, 0xC0, 0x4F, 0xA3, 0x30, 0xA6);

而後,用有意義的名字換掉<<name>>,如GUID_SIMPLE,並把這個定義包含到驅動程序或應用程序中。

 

 

/*此文是將網上的一些文章,自已遇到的問題進行整理,有些是翻譯,全部只供學習討論,若有版權還歸原做者*/

#32如何動態建立ocx?

答:看下面代碼

#include <atlbase.h>

 CComModule _Module;

#include <atlcom.h>

#include <atlwin.h>

#pragma comment(lib,"atl")

 

CComQIPtr<IWebBrowser2> m_spBrowser;

CAxWindow content_wnd;

......

if(  _tcslen(m_tcHtmlFileName) > 0 )

{

RECT rc;

GetClientRect( &rc );

if(m_spBrowser==NULL)

{

LPOLESTR pstrbrowserid;

StringFromCLSID(IID_IWebBrowser2,&pstrbrowserid);

_bstr_t bstrbrowser(pstrbrowserid);

CoTaskMemFree(pstrbrowserid);

if(content_wnd.IsWindow())

content_wnd.DestroyWindow();

content_wnd.Create( m_hWnd, rc, LPCTSTR(bstrbrowser), WS_CHILD&brvbar;WS_VISIBLE&brvbar;WS_HSCROLL&brvbar;WS_VSCROLL );//create a browser control

HRESULT hrbrowser;

hrbrowser = content_wnd.QueryControl( IID_IWebBrowser2, reinterpret_cast<void**>(&m_spBrowser) );

}

 

#33如何獲取窗體上ocx的接口指針?

解決方法:

CWindow::GetDlgControl()

或CAxWindow中的QueryControl

拿上面的例子:

hrbrowser = content_wnd.QueryControl( IID_IWebBrowser2, reinterpret_cast<void**>(&m_spBrowser) );

 

#34如何調整控件的大小

要調整控件大小,只需使用標準的 MoveWindow 或 SetWindowPos API(或它們的 CWindow 包裝)調整宿主窗口的大小便可。爲響應收到的窗口消息,宿主對象自動調整控件大小以填充宿主窗口。

 

#35 #import Java com組件後生成的.tlh文件中的漢字「參數」問題

解決辦法:

一、先保存好這個tlh文件的時間戳

二、打開.tlh文件,手動把"參數"改稱"var"

三、而後保存文件,並手動把時間戳改回1種保存的時間輟

f5-----ok!

 

#36如何使用IStream?

我之前寫的代碼:

void bufStorageWrite()

{

CoInitialize(NULL);

IStream *pStream = NULL;

HRESULThr = S_OK;

HGLOBAL hGLobal =  GlobalAlloc(GMEM_FIXED,SIZE_T(nCount*sizeof(Coord3D)));

::CreateStreamOnHGlobal(hGLobal,true,&pStream);

if(pStream)

{

DWORD dwData[2] = {1,0};

Coord3D coord = {1,2,3,4};

clock_t t1 = clock();

for(int i=0; i<nCount; i++)

{

pStream->Write(&coord,sizeof(Coord3D),NULL);//(&dwData, 2*sizeof(DWORD), NULL);

}

clock_t t2 = clock();

cout<<_T("bufStorageWrite WriteTime ")<<t2-t1<<endl;

 

pStream->Release();

}

 

CoUninitialize();

}

 

#37如何操做DATE類型?

看看下面代碼,加上#include <atlconv.h>

void CVarUseDlg::OnDate()

{

       VARIANT timeSelection;

       COleDateTime timeNow;

       DATE curDate;

       HRESULT hr;

       //獲取當前時間.

       timeNow = COleDateTime::GetCurrentTime();

      

       //設置一個時間給VARIANT

       timeSelection.vt = VT_DATE;

       timeSelection.date = timeNow.m_dt;

      

       //Convert Variant into string using Variant Change Type.

       hr = VariantChangeType(&timeSelection, &timeSelection, 0, VT_BSTR);

       CString sCurTime(timeSelection.bstrVal);

      

      

       //Get Time as System Time.

       SYSTEMTIME mySysTime;

       timeNow.GetAsSystemTime(mySysTime);

      

       //Use COleDateTime functionality to get change SYSTEMTIME into DATE.

       COleDateTime pastTime(mySysTime);

       curDate = pastTime.m_dt;

 

 

       //Use COldeDateTime Format command to get date as CString.

       LPCTSTR format = _T("%X %z");  //Current time and time zone.

       //Note see "strftime" help for valid formating strings.  

       CString sTime = pastTime.Format(format);

 

}

 

#38我用VB寫了一個DLL,用VC怎麼調用啊?

在#import "your.dll" no_namespace前加上下面這句:

#import "msvbvm60.dll" no_namespace rename("EOF", "EndOfFile") rename("RGB","ColorRGB")

使用VB來開發控件的時候,須要將VB的虛擬機裝上去。

msvbvm60是VB作的控件都要用到的一個DLL

 

#39 那怎麼發佈使用了dll的Activex呢?  

一、若是須要建立cab文件,首先須要Cabarc或者Makecab,它們隨着Cabinet  SDK的安裝就有了,Cabinet  SDK的下載地址是http://msdn.microsoft.com/workshop/management/cab/cabdl.asp。  
Cabarc能夠建立、查看或者解出cab裏面的文件,而Makecab則只能夠用來建立cab文件。  
二、製做cab文件時須要將全部的相關文件都包含進去,能夠經過Depends(VC自帶的)檢查須要的文件。使用inf文件將這些東西都寫進去。  
三、inf搞法:inf文件描述cab中全部的ocx及dll文件,inf經過一些命名區域來提供須要的信息。  
 
怎麼寫inf  
最開始通常是[Version]區:  
eg:  [Version]  
       signature="$XXXX$"  
       AdvancedINF=2.0  
接下來就是最重要的[Add.Code]區:  
eg:  [Add.Code]  
       Ctrl1.dll=C1Section  
       Ctrl2.dll=Ctrl2.dll  
前面是要下載的文件名,後面是對應這個文件的區域名,能夠是任何名字,不過通常都是和文件的名字相同,這樣方便維護。還有須要注意是在[Add.Code]區出現的文件要根據依賴性進行排序,例如前面說的ctrl1.dll要依賴於ctrl2.dll,則ctrl2.dll要出如今ctrl1.dll的前面。由於安裝時是按照相反的順序進行的,也就是說先安裝ctrl2.dll,而後纔是ctrl1.dll,哧哧,記清楚了,不要搞反了。  
再接下來是各個文件的區域了  
[Ctrl1.dll]  
file-win32-x86=thiscab  
RegisterServer=yes  
clsid={.....}  
DestDir=    
FileVersion=1,0,0,0  
[Ctrl1.dll]區域中的第一個file值告訴ie到哪裏去獲得這個dll,file一共包括三個部分,第一部分是file,這個永遠都是這樣的(至少目前來講);第二部分告訴聲明支持的OS,win32表示windows,mac就是蘋果MAC  OX了;第三部分是CPU類型,好比說x8六、  ppc  (Power  PC)、  mips或者alpha了。  
file的值能夠取三個一個URL、ignore和thiscab,若是是URL則說明到URL所在的位置去下;若是是ignore說明對於這種OS和CPU,不須要下載這個文件(ctrl1.dll);若是是thiscab很明顯就在當前的cab文件中了。  
接下來是RegisterServer,能夠取兩個值yes和no,若是爲yes則說明ie要註冊該dll,若是是no就沒必要了;  
再下來是DestDir,它的值是dll將要存到本地硬盤的位置,若是它的值是10,則將dll放到/Windows或者/WinNT下;若是是11,則放到/Windows/System或者  
/WinNT/System32下;若是是空(就是沒有值)則會放到/Windows或者/WinNT下的Downloaded  Program  Files目錄下;  
最後是FileVersion,這個就比較明顯了,說明了ctrl1.dll的版本號。  
有時候咱們使用VB來開發控件的時候,須要將VB的虛擬機裝上去,它須要一些其它的說明的,簡單地講一下吧:  
在[Add.Code]中增長一項MSVBVM60.DLL=MSVBVM60.DLL(以VB6爲例)下面是  
MSVBVM60.DLL區域:  
[MSVBVM60.DLL]  
           hook=MSVBVM60.cab_Installer  
           FileVersion=6,0,81,76  
FileVersion很明顯,是版本號,就再也不說發,就說說hook吧。  
hook區域是在安裝的時候須要執行的區域,它分爲兩種,一種是有條件的,另一種是無條件的,無條件的hook區域是必須執行的,反之則根據條件判斷是否執行。以[Setup  Hooks]標記的區域是無條件區域,以下所示  
[Setup  Hooks]  
       hookname=section-name  
 
[section-name]  
run=%EXTRACT_DIR%/setup.exe  
無條件區域經常使用來經過一個inf文件執行一個安裝程序,這就是咱們在資源管理器右鍵點擊一個inf文件時在執行安裝這樣的菜單的緣由了  
當ie下載了一個cab文件,若是文件中沒有[Add.Code],則處理[Setup  Hooks]區域,運行run所指定程序,哧哧,上面就是setup.exe;  
條件區域則爲在必定條件下執行,前面爲MSVBVM60.DLL指定的hook區域就是一個條件區域,若是在MSVBVM60.DLL指定的CLSID或者version不能知足須要並且沒有file這個命名值,則執行hook所指定的區域。  
[MSVBVM60.cab_Installer]  
file-win32-x86=http://activex.microsoft.com/controls/vb6/VBRun60.cab  
run=%EXTRACT_DIR%/VBRun60.exe  
上面[MSVBVM60.cab_Installer]是一個hook區域,它也包含了一個file值,指定一個URL,表示MSVBVM60.DLL能夠從這個URL下載獲得;run則說明了執行哪個文件  
這裏有必要說明一下的是,MS對一些經常使用的Redistributable  Microsoft  DLLs  
能夠經過指定CODEBASE屬性爲http://activex.microsoft.com/controls,這樣在cab文件中就中須要包含這些文件,在計算機上有一個文件redist.txt上面的dll就是Redistributable  Microsoft  DLLs  
 
 
建立一個cab文件:  
cabarc  N  ctrl1.cab  ctrl1.inf  ctrl1.dll  
N表示要建立一個新的文件,ctrl1.cab是建立的文件名,ctrl1.inf是cab的inf,後而是須要加到cab裏的文件,可使用通配符。  
而後就能夠將cab文件放到網頁上了  
<OBJECT  ID="Ctrl1Obj"  
               CLASSID="clsid:....................................."  
               CODEBASE="http://server.com/ctrl1.cab#version=8,0,0,5007">  
</OBJECT>  
這裏也在一個version,不過這裏的version是指控件的version,而inf裏的是文件的version。  
 
 
製做電子簽名:  
首先從下面的網址下載製做簽名的工具SignCode,地址是  
http://msdn.microsoft.com/workshop/gallery/tools/authenticode/authcode.asp  
從簽名受權中心如VeriSign或者你的局域網上運行的Microsoft  Certificate    
Server受權服務器獲得一個certificate,在申請受權的過程你會獲得一個私鑰。  
也可使用MakeCert.exe和Cert2Spc.exe建立的私鑰進行測試,方法是首先使用MakeCert建立一個X.509的certificate(.cer文件)  
       MakeCert  -sv  MyKey.pvk  n  "CN=My  Software  Company"  MyCert.cer  
而後利用Cert2Spc將.cer文件轉換成爲PKCS  #7軟件發佈Certificate(.spc文件),  
       Cert2Spc  MyCert.cer  MyCert.spc  
利用你下載的SignCode對你的cab文件進行電子簽名  
       SignCode  -spc  MyCert.spc  -v  MyKey.pvk  -t  http://  
               timestamp.verisign.com/scripts/timstamp.dll  ctrl1.cab  
SignCode還能夠指定一些其它的參數,就不說了,太長了,哧哧。  
雖然能夠利用測試的.cer和.spc文件,可是在發佈的時候,必須申請。  
 
其實東西在SDK中都有說明,不過都是E文的,慢慢看就沒有什麼發  
---------------------------------------------------------------  
 
再補充一點,若是隻是測試,你能夠SignCode時回車,根據提示一步步往下選,命令行參數又臭又長,還常常出錯,這樣會節省很多你的時間  
---------------------------------------------------------------  
 
這是由於ocx關聯了你的兩個dll,在系統調用ocx中的註冊函數是須要調用你的dll但系統沒法找到,因此加載ocx失敗形成註冊失敗  
 
解決的辦法是在cab的安裝文件裏把dll安裝到系統的目錄下,或者動態加載dll,或者指定加載dll的目錄

 

#40 如何在應用程序中判斷DLL(COM)已註冊?  
 
讀註冊表/HKEY_CLASSES_ROOT/APPID,看你的DLL是否註冊,  

代碼以下:

HKEY  valueKey;  
 if  (ERROR_SUCCESS!=RegOpenKeyEx(HKEY_CLASSES_ROOT,  
                       "?????",0,KEY_READ,&valueKey))    
 {           //?????爲類ID,如"CLSID//{3B5B0834-5D5D-46C9-AFC9-FD746EDCC272}"  
         //未註冊成功  
       return;  
  }  
  else  
             //已經註冊成功

 

#41 我想在程序中連續註冊好幾個控件,若是用regsvr32  命令會出現多個註冊成功的對話框有沒有什麼方法能夠解決那?

/u是註銷。  
/s是不彈出註冊成功的對話框   
regsvr32    ...  /s

代碼實現以下:

HMODULE  hModule  =  LoadLibrary(strFilePath);  
 
 if(hModule  ==  NULL)  
 {  
         strMessage.Format("LoadLibrary(/"%s/")  失敗!",strFilePath);  
        MessageBox(NULL,strMessage,"RegisterServer32",MB_ICONEXCLAMATION);  
         return  FALSE;  
 }  
  FARPROC  pFarProc  =  GetProcAddress(hModule,"DllRegisterServer");  
  if(pFarProc  ==  NULL)  
  {  
    MessageBox(NULL,"GetProcAddress(/"DllRegisterServer/")  Failed","RegisterServer32",MB_ICONEXCLAMATION);  
    }  
     else  
     {  
          (*pFarProc)();  
     }

 

 

#42 IWebBrowser2疑惑.,有時能獲取控件的句柄有時無法獲取,怎麼回事?

 

 

CComQIPtr<IWebBrowser2,  &IID_IWebBrowser2>  m_pWebBrowser2;  
//----------------------  使用CLSID_WebBrowser--------------------  
HRESULT  hrrs  =  CoCreateInstance(CLSID_WebBrowser,  NULL,CLSCTX_INPROC,IID_IWebBrowser2,(void**)&m_pWebBrowser2);  
//----------------------  使用CLSID_InternetExplorer---------------  
HRESULT  hrrs  =  CoCreateInstance(CLSID_InternetExplorer,  NULL,CLSCTX_INPROC,IID_IWebBrowser2,(void**)&m_pWebBrowser2);  
//----------------------  獲取句柄---------------------------------  
HWND  hIE;  
hrrs  =  m_pWebBrowser2->get_HWND((long*)&hIE);  
以上兩個方法來獲取IWebBrowser2,兩種都能獲取,但是第一種爲何得不到窗口句柄,而第二種能夠呢?請指教  ,有個麼辦法使第一種也能獲得Browser窗口句柄呢?  
---------------------------------------------------------------  
 解答:
CLSID_WebBrowser:是微軟web預覽控件的實現類的id,它同過屬性Parent來獲得窗口句柄,由於控件無法知道它被放在哪一個容器裏,因此不會實現HWND屬性!  
CLSID_InternetExplorer:是微軟ie瀏覽器自動化實現類的id,它經過屬性HWND獲得!由於獲得句柄必定封裝好了.  哈哈! 

 

 

#43 做了個ActiveX控件,嵌入網頁中使用,版本更新時,客戶端不能更新,如何解決?

 

 

網頁中調用控件部分代碼  
<OBJECT  classid="clsid:C69A0449-8786-11D4-B209-00104B13AFD4"  CODEBASE="PLWeb.cab#version=1,0,0,2"  height=605  id=PLWeb1  
                                                           style="LEFT:  0px;  TOP:  0px"  width=995  VIEWASTEXT>    
                                               <param  name="_Version"  value="65536">  
                                               <param  name="_ExtentX"  value="26326">  
                                               <param  name="_ExtentY"  value="16007">  
                                               <param  name="_StockProps"  value="0">  
                               </OBJECT>  
 
cab包中的內容  
plweb.ocx  (1.0.0.2版)  
plweb.inf  
(  
     [version]  
     signature="$CHICAGO$"  
     AdvancedINF=2.0  
 
     [Add.Code]  
     PLWeb.ocx=PLWeb.ocx  
 
     [PLWeb.ocx]  
     file-win32-x86=thiscab  
     ;  ***  add  your  controls  CLSID  here  ***  
     clsid={C69A0449-8786-11D4-B209-00104B13AFD4}  
     ;  Add  your  ocx's  file  version  here.  
     FileVersion=1,0,0,2  
     DestDir=11  
     RegisterServer=yes        
)  
客戶端在第一次瀏覽具備控件的網頁時,能夠正常下載註冊,但若是更新控件版本時,客戶端能夠下載(在internet的臨時文件夾中能夠找到新的plweb.cab文件),可是不能註冊新版本控件。  
       不知道這是什麼緣由引發的,如何解決?  
---------------------------------------------------------------  
 
<OBJECT  classid="clsid:C69A0449-8786-11D4-B209-00104B13AFD4"  CODEBASE="PLWeb.cab#version=1,0,0,3"  height=605  id=PLWeb1  
style="LEFT:  0px;  TOP:  0px"  width=995  VIEWASTEXT>  
 
你能夠手工在工程中更改版本號來達到升級   
 
若是沒有註冊成功,有多是客戶機環境變了.  
你的控件須要運行庫支持,如今客戶機沒有了[重作了系統等]  
打包時注意一下,若是是基於atl的,就打包ATL.dll,若是是基於mfc的就打包mfc42.dll和msvcrt.dll,若是須要ole就打包olepro32.dll和oleaut32.dll等.  
---------------------------------------------------------------  
 
檢查一下是否在修改程序時,將vc自動建立部分的代碼中的id修改了

 

 

#44 如何利用COM,用VC在word、Excel等文件中插入圖片?

 

 

http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q311/7/65.asp&NoWebContent=1  
 
在上面這個例子中,若是我想再加入在當前鼠標位置插入圖片的功能,該怎麼寫?  
---------------------------------------------------------------  
 
void  CWebOfficeCtrl::LoadUnit(void)  
{  
           AFX_MANAGE_STATE(AfxGetStaticModuleState());  
 
           //  TODO:  在此添加調度處理程序代碼  
           LPDISPATCH  lpDisp;  
           lpDisp=m_pWebOfficeFrame->m_pWebOfficeView->m_pSelection->GetIDispatch();  
           _Application_Word  m_WordApp;  
           _Document_Word  m_WordDoc;  
           m_WordDoc.AttachDispatch(lpDisp,TRUE);  
           //m_WordDoc.Activate();  
           m_WordApp=m_WordDoc.GetApplication();  
             
           InlineShapes_Word  m_WordInlineShapes;  
           InlineShape_Word  m_WordInlineShape;  
           Selection_Word  m_WordSelection;  
           m_WordSelection=m_WordApp.GetSelection();  
           m_WordInlineShapes=m_WordSelection.GetInlineShapes();  
           COleVariant  vTrue((short)TRUE),vFalse((short)FALSE),vOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);  
           //AfxMessageBox(m_UnitName,MB_ICONINFORMATION);  
           m_WordInlineShape=m_WordInlineShapes.AddPicture(m_UnitName,vFalse,vTrue,vOptional);  
           /*  
           //2003.11.29  Add  By  DigitalTitan[設置圖元重疊屬性]  
           Shape_Word  m_WordShape;  
           m_WordShape=m_WordInlineShape.ConvertToShape();  
           WrapFormat_Word  m_WordWrapFormat;  
           m_WordWrapFormat=m_WordShape.GetWrapFormat();  
           //m_WordWrapFormat.SetAllowOverlap(TRUE);3  
           m_WordWrapFormat.ReleaseDispatch();  
           m_WordShape.ReleaseDispatch();  
           //  
           */  
           m_WordInlineShape.ReleaseDispatch();  
           m_WordInlineShapes.ReleaseDispatch();  
           m_WordSelection.ReleaseDispatch();  
           m_WordDoc.ReleaseDispatch();  
           m_WordApp.ReleaseDispatch();  
}

 

 

當WORD已經啓動時,並打開了幾個文件,我想在其中的一個打開的WORD文件的光標位置插入一幅BMP  
           if(!WordApp.CreateDispatch("Word.Application",NULL))//建立一個新的word程序  
           {  
                       AfxMessageBox("建立ms_word服務失敗");  
                       exit(1);  
           }  
 
           //讓用戶可以查看自動化的過程  
           WordApp.SetVisible(true);  
 
           //讓docs和word程序鏈接,docs表明word中全部文檔  
           docs=WordApp.GetDocuments();  
           COleVariant  covOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);  
           //用docs來打開一個文檔,並將句柄返回給doc  
           //doc=docs.Add(COleVariant("C://ox.doc"),covOptional,covOptional,covOptional);  
//這裏要查找我要加入的WORD文件是否打開  
//怎麼樣在當前光標處加入文件.  
 
//  
 
---------------------------------------------------------------  
 
1.獲得ActivateDocument  
2.獲得ActivateDocument中的Shapes  
3.調用Shapes的方法:  AddPicture  
 
只要指定圖片文件名,就能夠加入圖片了。  
---------------------------------------------------------------  
 
InlineShapes_Word  m_WordInlineShapes;  
           InlineShape_Word  m_WordInlineShape;  
           Selection_Word  m_WordSelection;  
           m_WordSelection=m_WordApp.GetSelection();  
           m_WordInlineShapes=m_WordSelection.GetInlineShapes();  
           COleVariant  vTrue((short)TRUE),vFalse((short)FALSE),vOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);  
           //AfxMessageBox(m_UnitName,MB_ICONINFORMATION);  
           m_WordInlineShape=m_WordInlineShapes.AddPicture(m_UnitName,vFalse,vTrue,vOptional);  
           /*  
           //2003.11.29  Add  By  DigitalTitan[設置圖元重疊屬性]  
           Shape_Word  m_WordShape;  
           m_WordShape=m_WordInlineShape.ConvertToShape();  
           WrapFormat_Word  m_WordWrapFormat;  
           m_WordWrapFormat=m_WordShape.GetWrapFormat();  
           //m_WordWrapFormat.SetAllowOverlap(TRUE);3  
           m_WordWrapFormat.ReleaseDispatch();  
           m_WordShape.ReleaseDispatch();  
           //  
           */

#45 如何使用MFC插入圖片到excel?

#import  "G:/Program  Files/Common  Files/Microsoft  Shared/OFFICE11/MSO.DLL"  
#import  "G:/Program  Files/Common  Files/Microsoft  Shared/VBA/VBA6/VBE6EXT.OLB"  
#import  "G:/Program  Files/Microsoft  Office/OFFICE11/EXCEL.EXE"  rename("RGB",  "ExcelRGB")  rename("Delete","ExcelDelete")  rename("DialogBox","ExcelDialogBox")    
void  Cexcel_picDlg::OnBnClickedOk()  
{  
           //  insert  picture  to  excel  
           //  environment:  vc2003,  excel2003,  windows2003  
           using  namespace  Excel;  
           _variant_t  covOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR);  
           DWORD  dwStartTime  =  GetTickCount();  
           _ApplicationPtr  app;//("Excel.Application");  
           HRESULT  hr  =  S_OK;  
           CLSID  clsid;  
           CLSIDFromProgID(L"Excel.Application",  &clsid);    
           app.CreateInstance(clsid);  
           WorkbooksPtr  books;  
           _WorkbookPtr  book;  
           long  lcid  =LOCALE_USER_DEFAULT;  
           SheetsPtr  sheets;  
           app->get_Workbooks(&books);  
           book  =  books->Add(covOptional,lcid/*,&book*/);  
           book->get_Worksheets(&sheets);  
           IDispatchPtr  pDisp;  
           sheets->get_Item(_variant_t((short)1),&pDisp);  
           _WorksheetPtr  sheet(pDisp);  
           RangePtr  range  =  sheet->GetRange(_variant_t("A1"),_variant_t("A1"));  
           range->Value2  =  "aaa";  
           LPCSTR  m_Path  =  _T("C://flower8.jpg");  
           PicturesPtr  pics  =  sheet->Pictures();  
           pics->Insert(m_Path,VARIANT_FALSE);  
           app->put_AskToUpdateLinks(lcid,VARIANT_FALSE);  
           app->put_AlertBeforeOverwriting(lcid,VARIANT_FALSE);  
           app->put_UserControl(VARIANT_FALSE);  
           app->put_DisplayAlerts(0,VARIANT_FALSE);                          
           LPCTSTR  file_name  =  _T("c://aa.xls");  
           _variant_t  varfilename(file_name);  
           DeleteFile(file_name);  
           HRESULT  hrMethod  =  book->SaveAs(varfilename,covOptional,covOptional,covOptional,covOptional,covOptional,xlNoChange);  
           book->put_Saved(0,VARIANT_TRUE);                                                  
           book->Close(COleVariant(VARIANT_FALSE));  
           books->Close();              
           app->Quit();  
}

#46 關閉IE時,銷燬ACTIVEX控件的問題

 

 

我用VC嚮導建立  MFC  ACTIVEX  CONTROL,    
默認生成CMyTestApp,  CMyTestCtrl,  CMyTestPropPage三個類。  
 
而後,我在IE中調入該控件。當關閉IE時,CMyTestCtrl的OnDestroy和析構函數  
都不會被執行,在Container中調試時,卻都會被執行。請問這是爲何?  
(注:CMyTestApp的ExitInstance在關閉IE時,會被執行)  
 
那麼,我在CMyTestCtrl的構造函數或OnCreate中分配的資源,在哪兒釋放纔好呢?  
---------------------------------------------------------------  
 
重載WM_CLOSE或者WM_DESTROY  
---------------------------------------------------------------  
重載COleControl::OnClose  
對應於IOleControl::Close  
---------------------------------------------------------------  
WM_DESTROY

 

 

#47 ActiveX控件鍵盤輸入問題

本身開發了一個ActiveX控件,從COleControl繼承,在VB和Control  Test  Container中使用均沒有問題,可是在MFC的基於對話框的程序中使用,能響應鼠標輸入但不能響應鍵盤輸入。跟蹤發如今控件的PreTranslateMessage中有WM_CHAR消息但沒有響應OnChar函數,不知哪位大俠知道如何解決。  
 
---------------------------------------------------------------  
 
Accelerator  keys,  such  as  ARROW  keys,  are  first  received  by  the  message  pump  of  the  ActiveX  control's  container.  Even  if  the  control  has  the  focus,  it  does  not  receive  messages  for  keystrokes  that  have  special  meaning  to  control  containers,  such  as  ARROW  and  TAB  keys.  MFC  ActiveX  controls  have  a  chance  to  intercept  these  messages  by  overriding  their  PreTranslateMessage  function.    
 
However,  PreTranslateMessage  is  not  always  called  for  an  MFC  ActiveX  control.  
 
RESOLUTION  
Install  a  Windows  WH_GETMESSAGE  hook  for  the  modeless  dialog  box/propertysheet  derived  class  to  allow  it  to  intercept  keystrokes  and  handle  accelerators.    
...  
see  Knowledge  Base  articles  for  more  information  
Q168777  PRB:  MFC  ActiveX  Control  in  IE  Doesn't  Detect  Keystrokes  
Q180402  PRB:  MFC  ActiveX  Control  Ignores  ARROW  Keys  on  VB  Container  
Q187988  PRB:  ActiveX  Control  Is  the  Parent  Window  of  Modeless  Dialog  
Q199431  PRB:  Enabling  Menu  Mnemonics  in  an  MFC  ActiveX  Control  
Q194294  HOWTO:  Add  Toolbars  and  Tooltips  to  ActiveX  Controls  

 

 

#48 在ASP裏如何調試寫的(ATL寫的)COM呢?

 

首先在管理工具->組件服務裏面的「COM+應用程序」項下面添加一下空的應用程序,名字隨便,而後把寫好的組件註冊在這個裏面,並記下該應用程序的ID,如:{2D62D611-4A90-4196-AA9B-2055AD3A12E7}  
 
接下來在VC裏面project->setting->debug的executable  for  debug  session選項裏面填寫「系統目錄+DLLHOST.exe「,如個人系統填寫的是「C:/windows/system32/DLLHOST.EXE」;  
在program  arguments選項裏面填寫剛纔的應用程序ID  {2D62D611-4A90-4196-AA9B-2055AD3A12E7}  ,設置斷點,調試運行,你會發現COM程序會中斷下來,這樣就表示你設置成功了,而後用ASP調用就能夠了~  
---------------------------------------------------------------  
 
如下摘自  <windows  程序調試>  Addison  -Wesley  
     若是你寫的基com  dll要被在一個asp中運行的腳本調用,你的代碼將從一個配定組件中被調用。  當MS的internet信息服務器處理一個來自ASP的請求時,他激活一個叫作網絡應用管理器(web  application  manager)的配定組件。  這個組件執行該ASP中的腳本發出的激活和方法調用請求。由於網絡應用管理組件是在MTS/COM+配置的。你的com  dll會在一個代理進程的環境中執行。  
   在這個狀況下,將調試器附到正確的代理進程的工做就變成了肯定哪一個服務器包是你的組件宿主的問題。  。。。  
if  on  windows  2000  
     在WIN2000中,管理工具->Internet  服務管理器->查看你的虛擬站點的屬性->在指定虛擬目錄的tab頁中,包含一個叫應用程序保護的選項。(在最下面)  
     這個選項可設置爲:  
1)LOW  (低,IID進程)  
2)Medium(中等,對象池)  
3)high(高,獨立)  
這個設置決定組件的宿主服務器包。  
 
if  1),  在COM+瀏覽器中找到in-process  apllication包的GUID  
if  2),在com+瀏覽器中找到IIS  out-of-process  pooled  apllication包的GUID  
if  3),在com+瀏覽器中找到IIS-{website//你的虛擬目錄名}包的GUID  
 
而後,你能夠參考上面的答覆,將VC附到已運行的代理進程,進行調試了。

#49 初學者應該知道的一些COM的基本概念

小弟接觸COM也有一段時間了,對不少COM相關的概念還沒弄清楚,查了許多資料,資料上也講得迷迷糊糊的,因此向你們請教一下:  
       1、常常看到DLL、OLE、ActiveX、COM等技術資料,他們之間究竟是些什麼關係,有什麼區別與聯繫?小弟只知道ActiveX與COM有密切的聯繫,而COM又以DLL文件形式存在,ActiveX又以.ocx形式存在,對OLE究竟是什麼東西有什麼用也沒弄明白。  
       2、對於GUID、CLSID常常弄不清楚,(他們的樣子很相象),做用也不太明白。好象COM必定要在註冊表註冊,不知OLE、COM組件是否必定須要註冊啊?不註冊能用嗎?  
 
 望各位高手能詳細指教一下(不要笑話小弟白癡啊,我真的沒弄明白)  
---------------------------------------------------------------  
 
COM是一個二進制標準,它是以DLL形式存儲的,因爲傳統DLL沒有解決一些諸如內存分配,卸載等關鍵性問題,因此它不足以成爲一個組件模型,而COM定義了這些標準。OLE算是COM的前身,到OLE2後就正式出現爲COM標準,它已經在OLE(對象鏈接與嵌入)上做了極大的改進,成爲一個獨立的標準,而ActiveX是微軟提出的一個專有名詞,目的是實現網上的一些應用,它的本質就是COM。  
CLSID是用來標誌每一個COM組件的,CLSID是屬於GUID的,格式同樣,只是CLSID有特定用途,換了個名字而已,COM必定要註冊,不註冊的話就找不到他的具體位置,好像.net之後就不須要註冊了。  
我也不太懂,一塊兒學習  
---------------------------------------------------------------  
 
上面的說的很好!我補充一點吧!  
CLSID能夠這麼解釋class  id,GUID是全局惟一ID,CLSID也是屬於GUID的,例如IID是Interface  ID。多看看COM本質!  
 
DLL,OLE->COM->ActiveX,ATL->COM+,DCOM->如今出現.NET中的公共語言環境。  
這是我本身的理解,但願對你有幫助!  
---------------------------------------------------------------  
 
COM是一種標準,巧妙的利用了運行時綁定的技術。能夠DLL形式存在,也能夠exe形式存在。  
ActiveX是COM標準的一個具體應用而已。ocx文件就是動態鏈接庫文件,擴展名不一樣而已。OLE也是一種技術的名稱,叫對象鏈接與嵌入,主要目的是經過一套約定,來使得不一樣程序能相互傳遞數據。ole2之後都是經過的COM技術來實現OLE了。  
 
GUID是一種常量,這種常量能保證世界上任何地方的任何人在任什麼時候候都能產生一個不一樣於其餘人的值。CLSID就是GUID,只不過看起來明確一點,他是指代class的。相似的還有IID,是指代interface的。  
就好像UINT  就是  unsigned  long。只不過看起來舒服些。  

  

#50 如何修改DCOM應用程序的運行位置(在哪臺機器上運行)?

 

 

DCOMCNFG.exe中某應用程序「位置」項,選了「在這臺計算機上運行應用程序」則「在下列計算機上運行應用程序」的設置就不生效了。  
請問如何不用DCOMCNFG.exe而經過修改程序將「在這臺計算機上運行應用程序」項前面的對號去掉(如操做註冊表,改哪一項的制值)?  
---------------------------------------------------------------  
 
用CoCreateInstanceEx,在參數pServerInfo中,寫入服務器的地址  
---------------------------------------------------------------  
 
固然有,經過dcom的管理接口  
---------------------------------------------------------------  
 
在註冊表中你的服務器AppID下加一個RemoteServerName字符串值"www.xxx.com"或IP地址  
也可使用dcomcnfg設置

#50 如何修改DCOM應用程序的運行位置(在哪臺機器上運行)?

 

 

DCOMCNFG.exe中某應用程序「位置」項,選了「在這臺計算機上運行應用程序」則「在下列計算機上運行應用程序」的設置就不生效了。  
請問如何不用DCOMCNFG.exe而經過修改程序將「在這臺計算機上運行應用程序」項前面的對號去掉(如操做註冊表,改哪一項的制值)?  
---------------------------------------------------------------  
 
用CoCreateInstanceEx,在參數pServerInfo中,寫入服務器的地址  
---------------------------------------------------------------  
 
固然有,經過dcom的管理接口  
---------------------------------------------------------------  
 
在註冊表中你的服務器AppID下加一個RemoteServerName字符串值"www.xxx.com"或IP地址  
也可使用dcomcnfg設置

  

#50 如何修改DCOM應用程序的運行位置(在哪臺機器上運行)?

 

 

DCOMCNFG.exe中某應用程序「位置」項,選了「在這臺計算機上運行應用程序」則「在下列計算機上運行應用程序」的設置就不生效了。  
請問如何不用DCOMCNFG.exe而經過修改程序將「在這臺計算機上運行應用程序」項前面的對號去掉(如操做註冊表,改哪一項的制值)?  
---------------------------------------------------------------  
 
用CoCreateInstanceEx,在參數pServerInfo中,寫入服務器的地址  
---------------------------------------------------------------  
 
固然有,經過dcom的管理接口  
---------------------------------------------------------------  
 
在註冊表中你的服務器AppID下加一個RemoteServerName字符串值"www.xxx.com"或IP地址  
也可使用dcomcnfg設置

 

 轉貼請註明出處!

相關文章
相關標籤/搜索