Winform引用Activex控件

今天写了两个简单的Activex控件,一个用的MFC,一个用的ATL

用MFC的控件,编译后,直接引用就可以使用了。

用ATL写的控件,编译后,可以添加到VC程序中使用,但在WinForm程序中不可以使用,从工具箱拖拽到form后会报下面的错误:

Failed to create component 'AxHost'.  The error message follows:
 'System.InvalidOperationException: Unable to initialize component.
   at System.Windows.Forms.AxHost.DepersistControl()
   at System.Windows.Forms.AxHost.ActivateAxControl()
   at System.Windows.Forms.AxHost.TransitionUpTo(Int32 state)
   at System.Windows.Forms.AxHost.CreateHandle()
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl()
   at System.Windows.Forms.Control.ControlCollection.Add(Control value)
   at System.Windows.Forms.Form.ControlCollection.Add(Control value)
   at System.Windows.Forms.Design.ParentControlDesigner.AddChildControl(Control newChild)
   at System.Windows.Forms.Design.ParentControlDesigner.AddControl(Control newChild, IDictionary defaultValues)
   at System.Windows.Forms.Design.ControlDesigner.InitializeNewComponent(IDictionary defaultValues)
   at System.Windows.Forms.Design.AxHostDesigner.InitializeNewComponent(IDictionary defaultValues)'

查找了半天,有人说用AxImp.exe生产一个代理类可以解决这个问题。
尝试了SDK7.0,SDK7.1,SDK8.0,SDK8.1的AxImp.exe,可以成功生成dll,但是仍然没法使用。

也有人说,可以通过降低.NetFramework版本解决这个问题,2.0,3.0,3.5我都试了下,都不行。

试了VS2010和VS2013都不行,累了,回去了。。。

明天有空再弄吧。。。

今天试了一下,微软你大爷的,同样的操作,VS2008上(XP和Win7)就是好的。。。
真够坑爹啊。。。

再次测试表明:
1、VS2008的ATL控件在高版本的VS上可以插入到WinForm
2、VS2010以上的ATL控件,不能插入到VS2008以上任何版本的WinForm

VS2010以上的ATL控件,插入VS2008时,报的错误与上面不同

Debug Assertion Failed!
Program:.......
File:......vc\atlmfc\include\atlctl.h

Expression:pClientSite == 0 || m_spClientSite == 0

For information on how ....

微软,你自己的产品,兼容性做好一些,你能死啊。。。

ActiveX控件使用SDI特性(下)

放假回来,接着写:)

这两个类的使用步骤如下:
1、使用向导生成MFC ActiveX控件,(假设控件类为NControlCtrl)
2、工程中添加上面两个文件,ActivDoc.cpp和ActivDoc.h
3、将控件的基类,从COleControl换为CActiveXDocControl,在NControlCtrl.h中添加ActivDoc.h的引用
4、将NControlCtrl控件类中的COleControl全部替换为CActiveXDocControl
5、去掉NControlCtrl的OnDraw函数
5、将自己的Document类,View类,Frame类,添加到项目中,(假设为NDocument,NView,NFrame)
6、在NControlCtrl类中,添加NDocument,NView,NFrame的引用
7、在类的构造函数中,添加一行代码

	AddDocTemplate(new CActiveXDocTemplate(
		RUNTIME_CLASS(NDocument), 
		RUNTIME_CLASS(NMainFrame), 
		RUNTIME_CLASS(NView)));

8、这样SDI就可以用了。如果你原来的项目比较复杂的话,就要将资源文件进行拷贝及合并了,注意ID不要重复哦。

后记:
尝试了直接将NMainFrame进行分栏,但整个框架对于NDocument和NView比较依赖,会有报错。
最近比较忙,只好有空再搞下了。

ActiveX控件使用SDI特性(上)

大家都知道,使用微软的MFC ActiveX控件向导生成的控件类,是基于COleControl的,而COleControl是基于CWnd类。这样很多MFC的特性,如CFrameWnd的特性,CSingleDocTemplate(SDI)的特性等就无法使用了。如果要把用到这些类的MFC程序封装为ActiveX插件,就会有不少麻烦。

其实这个问题,在上个世纪MFC诞生不久,就有人解决了,并提供了完整的示例代码。微软官方的这篇文章《Designing ActiveX Components with the MFC Document/View Model》,在原地址http://www.microsoft.com/mind/0497/mfc.asp已经无法找到了。

但好在这里还有完整的备份《Designing ActiveX Components with the MFC Document/View Model》

原文的作者,实现了CActiveXDocTemplate和CActiveXDocControl两个类,而用户只需要将控件的基类从COleControl换为CActiveXDocControl,并做简单修改,就可以方便的使用SDI的特性了。

整体思路其实是使用CActiveXDocControl扩展了COleControl。在CActiveXDocControl中,作者初始化了CDocManager,使用CActiveXDocTemplate控制了document、view、frame的生成,处理了OnSize事件,并使用Timer定时获取消息处理更新。

我们使用的时候,主要就是用这两个类,代码如下(两个类相互引用,所以放到了同一文件中):

1、ActivDoc.h

class CActiveXDocTemplate : public CSingleDocTemplate
{
    enum { IDR_NOTUSED = 0x7FFF };

    CWnd* m_pParentWnd;
    CFrameWnd* m_pFrameWnd;
    CString m_docFile;

public:
    CActiveXDocTemplate(CRuntimeClass* pDocClass,
        CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass);

    CFrameWnd* CreateDocViewFrame(CWnd* pParentWnd);
    void SaveDocumentFile();

    virtual CFrameWnd* CreateNewFrame(CDocument* pDoc,
        CFrameWnd* pOther);
    virtual CDocument* OpenDocumentFile(
        LPCTSTR lpszPathName, BOOL bVerifyExists = TRUE);
};

//===================================================

class CActiveXDocControl : public COleControl
{
    enum { WM_IDLEUPDATECMDUI = 0x0363 };

    static BOOL m_bDocInitialized;
    CActiveXDocTemplate* m_pDocTemplate;
    CFrameWnd* m_pFrameWnd;

    DECLARE_DYNAMIC(CActiveXDocControl)

protected:
    void AddDocTemplate(CActiveXDocTemplate* pDocTemplate);
    CDocTemplate* GetDocTemplate() { return m_pDocTemplate; }

    //{{AFX_MSG(CActiveXDocControl)
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnTimer(UINT nIDEvent);
    afx_msg void OnDestroy();
    //}}AFX_MSG
    //{{AFX_DISPATCH(CActiveXDocControl)
    //}}AFX_DISPATCH
    //{{AFX_EVENT(CActiveXDocControl)
    //}}AFX_EVENT

    DECLARE_MESSAGE_MAP()
    DECLARE_DISPATCH_MAP()
    DECLARE_EVENT_MAP()

public:
    CActiveXDocControl();
    virtual ~CActiveXDocControl();

    enum {
    //{{AFX_DISP_ID(CActiveXDocControl)
    //}}AFX_DISP_ID
    };
};

2、ActivDoc.cpp

// ActivDoc.cpp : implementation file
//

#include "stdafx.h"
#include "ActivDoc.h"

CActiveXDocTemplate::CActiveXDocTemplate(CRuntimeClass* pDocClass,
    CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)
        : CSingleDocTemplate(IDR_NOTUSED, pDocClass, pFrameClass,
            pViewClass)
{
    ASSERT(pFrameClass);
}

CFrameWnd* CActiveXDocTemplate::CreateDocViewFrame(CWnd* pParentWnd)
{
    ASSERT(pParentWnd && IsWindow(*pParentWnd));
    ASSERT_KINDOF(CActiveXDocControl, pParentWnd);
    m_pParentWnd = pParentWnd;
    m_pFrameWnd = NULL;

    // OpenDocumentFile is a virtual method (implemented in
    // the CSingleDocTemplate base class) that creates
    // the document (either empty or loaded from the specified
    // file) and calls our CreateNewFrame function. Passing
    // NULL to the function creates a new document. Incidentally,
    // the view is created by the CFrameWnd's OnCreateClient()
    // method.

    if (!OpenDocumentFile(NULL))
        return NULL;

    // Since OpenDocumentFile sets m_pFrame, we can now use it.

    ASSERT(m_pFrameWnd);
    ASSERT_KINDOF(CFrameWnd, m_pFrameWnd);
    m_pFrameWnd->ShowWindow(SW_SHOWNORMAL);
    return m_pFrameWnd;
}

CFrameWnd* CActiveXDocTemplate::CreateNewFrame(CDocument* pDoc,
        CFrameWnd* pOther)
{
    ASSERT(pOther == NULL);
    ASSERT(m_pFrameClass != NULL);
    if (pDoc != NULL)
        ASSERT_VALID(pDoc);

    // Create a frame wired to the specified document

    CCreateContext context;
    context.m_pCurrentFrame = pOther;
    context.m_pCurrentDoc = pDoc;
    context.m_pNewViewClass = m_pViewClass;
    context.m_pNewDocTemplate = this;

    m_pFrameWnd = (CFrameWnd*)m_pFrameClass->CreateObject();
    if (m_pFrameWnd == NULL)
    {
        TRACE1("Warning: Dynamic create of frame %hs failed.\n",
            m_pFrameClass->m_lpszClassName);
        return NULL;
    }
    ASSERT_KINDOF(CFrameWnd, m_pFrameWnd);

    if (context.m_pNewViewClass == NULL)
        TRACE0("Warning: creating frame with no default view.\n");

    // The frame is created as a menu-less child of the
    // CActiveXDocControl in which it will reside.

    ASSERT_KINDOF(CActiveXDocControl, m_pParentWnd);
    if (!m_pFrameWnd->Create(NULL, "", WS_CHILD|WS_VISIBLE,
        CFrameWnd::rectDefault, m_pParentWnd, NULL, 0, &context))
    {
        TRACE0("Warning: CDocTemplate couldn't create a frame.\n");
        return NULL;
    }

    return m_pFrameWnd;
}

CDocument* CActiveXDocTemplate::OpenDocumentFile(
    LPCTSTR lpszPathName, BOOL bVerifyExists)
{
    SaveDocumentFile();
    m_docFile = lpszPathName;

    if (bVerifyExists)
    {
        DWORD dwAttrib = GetFileAttributes(m_docFile);
        if (dwAttrib == 0xFFFFFFFF ||
            dwAttrib == FILE_ATTRIBUTE_DIRECTORY)
        {
            lpszPathName = NULL;
        }
    }

    return CSingleDocTemplate::OpenDocumentFile(
        lpszPathName, TRUE);
}

void CActiveXDocTemplate::SaveDocumentFile()
{
    if (m_pOnlyDoc != NULL)
    {
        if (!m_docFile.IsEmpty())
            m_pOnlyDoc->OnSaveDocument(m_docFile);
        else
            m_pOnlyDoc->SetModifiedFlag(FALSE);
    }
}

//===================================================
// CActiveXDocControl

IMPLEMENT_DYNAMIC(CActiveXDocControl, COleControl)
BEGIN_MESSAGE_MAP(CActiveXDocControl, COleControl)
    //{{AFX_MSG_MAP(CActiveXDocControl)
    ON_WM_CREATE()
    ON_WM_SIZE()
    ON_WM_TIMER()
    ON_WM_DESTROY()
    //}}AFX_MSG_MAP
    ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()
BEGIN_DISPATCH_MAP(CActiveXDocControl, COleControl)
    //{{AFX_DISPATCH_MAP(CActiveXDocControl)
    //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
BEGIN_EVENT_MAP(CActiveXDocControl, COleControl)
    //{{AFX_EVENT_MAP(COleFrameCtrl)
    //}}AFX_EVENT_MAP
END_EVENT_MAP()

int CActiveXDocControl::m_bDocInitialized = FALSE;

CActiveXDocControl::CActiveXDocControl()
{
    m_pDocTemplate = NULL;
    m_pFrameWnd = NULL;

    // Since we're in an OCX, CWinApp::InitApplication() is
    // not called by the framework. Unfortunately, that method
    // performs CDocManager initialization that is necessary
    // in order for our DocTemplate to clean up after itself.
    // We simulate the same initialization by calling the
    // following code the first time we create a control.

    if (!m_bDocInitialized)
    {
        CDocManager docManager;
        docManager.AddDocTemplate(NULL);
        m_bDocInitialized = TRUE;
    }
}

CActiveXDocControl::~CActiveXDocControl()
{
    // Note that the frame, the document, and the view are
    // all deleted automatically by the framework!
    
    delete m_pDocTemplate;
}

void CActiveXDocControl::AddDocTemplate(CActiveXDocTemplate* pDocTemplate)
{
    // I've decided to call this function AddDocTemplate to
    // be consistent with naming of CWinApp::AddDocTemplate.
    // However, only one DocTemplate is allowed per control.
    
    ASSERT(pDocTemplate);
    ASSERT(m_pDocTemplate == NULL);
    m_pDocTemplate = pDocTemplate;
}

int CActiveXDocControl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (COleControl::OnCreate(lpCreateStruct) == -1)
        return -1;

    // The CActiveXDocTemplate object will create the
    // document, the view, and the frame inside the
    // control. The reason we need a handle to the frame
    // is so that we can resize it when the control is
    // resized.
    
    ASSERT(m_pDocTemplate);    // Set in call to AddDocTemplate
    m_pFrameWnd = m_pDocTemplate->CreateDocViewFrame(this);
    ASSERT_KINDOF(CFrameWnd, m_pFrameWnd);

    // By default, we'll create the control with a border,
    // since it looks better for frames containing a toolbar.

    SetBorderStyle(TRUE);
    SetTimer(WM_IDLEUPDATECMDUI, 300, NULL);
    return 0;
}

void CActiveXDocControl::OnSize(UINT nType, int cx, int cy) 
{
    COleControl::OnSize(nType, cx, cy);

    // The CFrameWnd should always fill up the entire client
    // area of the control.

    if (m_pFrameWnd != NULL)
    {
        ASSERT(IsWindow(*m_pFrameWnd));
        CRect area;
        GetClientRect(area);
        m_pFrameWnd->MoveWindow(area.left, area.top, area.right,
            area.bottom);
    }
}

void CActiveXDocControl::OnTimer(UINT nIDEvent) 
{
    // Since we're in an OCX, we don't control the message loop,
    // so CWinThread::OnIdle is never called. That means we have
    // to periodically pump the ON_UPDATE_COMMAND_UI messages
    // by hand.
    
    SendMessageToDescendants(WM_IDLEUPDATECMDUI, TRUE);
    COleControl::OnTimer(nIDEvent);
}


void CActiveXDocControl::OnDestroy() 
{
    AfxGetApp()->m_pMainWnd = NULL;
    m_pDocTemplate->SaveDocumentFile();
    COleControl::OnDestroy();
}

困了,回去再写。。。

div被activex控件遮住的处理方法

不知什么时候,自己成了写JS的人了

说实话,用过很多种编程语言,但至今发现,最佩服的莫过于JS程序员,调试那叫个烦人啊,写出JQuery的那些人,要么有更好的调试工具,要么就是真正的牛人吧。
JS的调试工具,IE下,直接用IE的调试工具(IE7及以上)或者是Companion.JS
在FF下,自然是FireBug老兄了

现在,主界面上已经集成了JQuery,JQueryUI,JX,SuperFish
还记得当初把JX放到JQuery中,以及修改JX.Tree增加叶子节点的ID等属性,加起来痛苦了有一周左右,终于把JX删的删,改的改,满足了需求。

这两天呢,又遇到了SuperFish的菜单,被一个第三方ActiveX控件遮住的问题,让大家痛苦了许久。
网上找呢,也全是关于flash的东西,只要设个参数就好了。

没办法,只好去修改SuperFish的代码咯。
具体的思路是:

在菜单弹出后,弹出菜单的下方,生成一个同样大小的iframe,

var mydoc=window.frames["FrameId"].document;
var objiframe = mydoc.createElement("iframe");
objiframe.id ="IFuLeU";
objiframe.style.visibility = 'hidden';
objiframe.style.top = 0;
objiframe.style.left = 0;
objiframe.style.width = 0;
objiframe.style.height = 0;
mydoc.body.appendChild(objiframe);

objiframe.style.position = "absolute";
objiframe.style.top = 0;
objiframe.style.left = this.offset().left-1;
objiframe.style.width = this.width();
objiframe.style.height = this.height()-2;
objiframe.style.visibility = 'visible';

在菜单隐藏时,将iframe销毁,

var mydoc=window.frames["FrameId"].document;
var objiframe = mydoc.getElementById("IFuLeU");
mydoc.body.removeChild(objiframe);

但这样呢,还会有个小问题
就是在菜单弹出后,会有个较为明显的先被ActiveX遮住,然后菜单又将Activex遮住的过程。