如果妳希望見到我們為Notepad++編寫的插件的全部源碼,請訪問我們的插件主頁。
使用C#語言創建ActiveX控件
這部分的解決方案是基於由Morgan Skinner發表的文章《以ActiveX控件的形式暴露Windows窗體控件》。盡管Skinner提供他的解決方案是由Visual Studio 8 Beta版本開發的,但他的例子在VS8的發行版運行得也很好(只是做了小小改動)。下面是我們對Skinner的解決方案所作的改動的壹個列表:
1. 設置ClassInterface為ClassInterfaceType.None(這樣我們暴露唯壹指定的接口到COM)。更多的信息在下壹章。
2. 工程應該被設置為visible to COM。具體是這樣做的:在“Project Properties”菜單項上的“Application”選項卡上的Assembly Information對話框上的“Make assembly COM visible”被選中
1. 妳還應該註冊工程為COM互操作(請註意在VS8.0的發行版,“Build”屬性窗口和beta版本有著不同的設計)。當這個特性被選上,當工程成功編譯之後Visual Studio將自動註冊.NET ActiveX控件。(具體如下圖)
4. 在Skinner的文章中,在ComUnregisterFunction()函數中有壹個小錯誤。下面是正確的函數:
///
/// Unregister ActiveX DLL function
///
///
[ComUnregisterFunction()]
public static void UnregisterClass(string i_Key)
{
// strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
StringBuilder sb = new StringBuilder(i_Key);
sb.Replace(@"HKEY_CLASSES_ROOT\", "");
// open HKCR\CLSID\{guid} for write access
RegistryKey registerKey =
Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);
// delete the 'Control' key,
// but don't throw an exception if it does not exist
registerKey.DeleteSubKey("Control", false);
// next open up InprocServer32
RegistryKey inprocServer32 =
registerKey.OpenSubKey("InprocServer32", true);
// and delete the CodeBase key, again not throwing if missing
inprocServer32.DeleteSubKey("CodeBase", false);
// finally close the main key
registerKey.Close();
}
為COM導出明確的方法
為了進行更為精確的設計,我們為COM導出指定的方法。每壹個使用我們控件的外部程序都將僅僅訪問它們必需的方法。
導出具體方法的最好方法是為包含所有相關方法創建壹個接口。那麽,具體的屬性應該被添加到這個接口。Form類應該在這個接口實現。
///
/// COM Interface - enables to run c# code from c++
///
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ICSSExplorerInterface
{
void setButtonCaption(String strNewCaption);
void setAdapterDllPtr(IntPtr i_AdapterDllPtr);
}
我們使用微軟消息體系和VC工程的容器窗口和其他窗口進行通信。我們不處理任何事件,因為如果這樣它將變得更為復雜並且對於我們的解決方案它不是必需的。
我們增加下面代碼到我們的MyDotNetActiveX類去允許消息傳輸:
private static uint DOT_NET_BUTTON_PRESSED = 0x0400;
private void btnOK_Click(object sender, EventArgs e)
{
SendMessage(m_AdapterDllPtr.ToInt32(),
DOT_NET_BUTTON_PRESSED, IntPtr.Zero, IntPtr.Zero);
}
#region MAPPING_OF_USER32_DLL_SECTION
[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern IntPtr SendMessage(
int hwnd, uint wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(
int hwnd, uint wMsg, int wParam, string lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(
int hwnd, uint wMsg, int wParam, out int lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern int GetNbFiles(
int hwnd, uint wMsg, int wParam, int lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern int GetFileNames(
int hwnd, uint wMsg,
[MarshalAs(UnmanagedType.LPArray)]IntPtr[] wParam,
int lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(
int hwnd, uint wMsg, int wParam, StringBuilder lParam);
#endregion
在初始化代碼中,我們假設我們的容器窗口將傳給它的窗口句柄(hwnd參數)來通訊。
現在我們準備編譯和測試這個控件。Visual Studio在成功編譯之後將自動註冊我們的ActiveX控件。妳可以通過免費軟件RegDllView查看註冊信息。
在ActiveX控件測試容器中測試控件
在我們跳到這篇文章的下壹步之前,在第三方程序中測試我們的控件是壹段美好時光。我們使用ActiveX控件測試容器(tstcon32.exe)進行測試。這個程序在Visual Studio的安裝目錄下是可以找得到的。
1. 通過“Edit”菜單欄的“Insert New Control”菜單項插入控件
2. 現在選擇“Control”菜單欄中的“Invoke Methods”菜單項。
3. 在Method Name combo-box控件選擇setButtonCaption函數。
4. 在Parameter Value文本框中輸入“Hello”並按下“Invoke”按鈕
5. 下面是測試結果
添加 C# ActiveX 控件to C++ 窗口
使用ATL Control Containment
任何通過使用Active Template Library (ATL)都可以包含ActiveX控件。
在這部分指南裏,我們將完成下面工作:
創建壹個C++ Win32 Application 工程
插入 our ActiveX 控件到C++ 窗口
發送命令到 ActiveX控件
從我們的 ActiveX控件中接收消息
創建 C++ Win32 Application 工程
創建壹個新的 Win32 工程並命名為“CPP_Container:”:
2.采用默認設置並按下“OK”按鈕:
插入 C# ActiveX 控件到C++ 窗口
添加下面代碼到CPP_Container.cpp開頭:
復制代碼
#define DOT_NET_BUTTON_PRESSED 0x0400
HWND _hAtl;
HWND _hSelf;
IUnknown* _pUnk;
DotNetActiveX::ICSSExplorerInterfacePtr _pDotNetCOMPtr;
HINSTANCE _hWebLib = ::LoadLibrary(TEXT("ATL.DLL"));
復制代碼
2.當Visual Studio編譯完我們的C#工程,它創建了DotNetActiveX.tlb文件。這個文件包含了這個工程的所有方法和結構體。我們將通過下面命令導入這些數據:
// import C# control function and structures
#import "DotNetActiveX.tlb" named_guids raw_interfaces_only
3.添加下面函數到CPP_Container.cpp。這個函數插入ATL容器到窗口並加載我們的C# ActiveX控件:
void loadActiveX(LPCTSTR strActiveXName)
{
//Initialize ATL control containment code.
BOOL (WINAPI *m_AtlAxWinInit)();
m_AtlAxWinInit = (BOOL (WINAPI *)(void))::GetProcAddress
(_hWebLib, "AtlAxWinInit");
m_AtlAxWinInit();
// Get the dimensions of the main window's client
// area, and enumerate the child windows. Pass the
// dimensions to the child windows during enumeration.
RECT rcClient;
GetClientRect(_hSelf, &rcClient);
_hAtl = ::CreateWindowEx(
WS_EX_CLIENTEDGE,\
TEXT("AtlAxWin"),\
strActiveXName,\
WS_CHILD | WS_VISIBLE | /*WS_CLIPCHILDREN | */WS_EX_RTLREADING,\
0, 0, rcClient.right, rcClient.bottom,\
_hSelf,\
NULL,\
NULL,\
NULL);
if (!_hAtl)
{
MessageBox( NULL, TEXT("Can not load AtlAxWin!"),
szTitle, MB_OK | MB_ICONSTOP);
throw int(106901);
}
HRESULT (WINAPI *m_AtlAxGetControl) (HWND h, IUnknown** pp);
m_AtlAxGetControl = (HRESULT (WINAPI *)
(HWND, IUnknown**))::GetProcAddress(_hWebLib, "AtlAxGetControl");
m_AtlAxGetControl(_hAtl, &_pUnk);
_pUnk->QueryInterface(__uuidof(DotNetActiveX::ICSSExplorerInterface),
(LPVOID *) &_pDotNetCOMPtr);
if (_pDotNetCOMPtr != NULL)
{
_pDotNetCOMPtr->setAdapterDllPtr((long) _hSelf);
}
else
{
// Get the dimensions of the main window's client
// area, and enumerate the child windows. Pass the
// dimensions to the child windows during enumeration.
RECT rcClient;
GetClientRect(_hSelf, &rcClient);
::DestroyWindow(_hAtl);
_hAtl = ::CreateWindowEx(
WS_EX_CLIENTEDGE,\
TEXT("AtlAxWin"),\
TEXT("MSHTML:""Please register ActiveX
control before using this plugin."""),\
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
WS_EX_RTLREADING,\
0, 0, rcClient.right, rcClient.bottom,\
_hSelf,\
NULL,\
NULL,\
NULL);
}
}
復制代碼
4.為了更準確的開發,在WndProc函數中添加下面代碼到WM_DESTORY消息處理塊中(註:為了銷毀C# ActiveX控件窗口和釋放加載的內存)。
_pDotNetCOMPtr->Release();
::DestroyWindow(_hAtl);
_pUnk->Release();
::FreeLibrary(_hWebLib);
5. 最後在_tWinMain函數調用loadActiveX函數。
loadActiveX(TEXT("DotNetActiveX.MyDotNetActiveX"));
發送命令到C# ActiveX控件
在插入TLB文件之後,我們在C#工程中導出的所有方法將會顯示。現在我們簡單地調用相關的方法:
char *strHelloWorld = "Hello World!";
_bstr_t bstrHelloWorld(strHelloWorld);
_pDotNetCOMPtr->setButtonCaption(bstrHelloWorld);
這將會把按鈕的標題改為“Hello World!”。
通過微軟的消息體系從C#控件的消息能夠到達C++窗口。通過調用loadActiveX函數我們已經將我們的窗口句柄發給C#控件。所以現在,我們僅僅需要在WndProc函數添加壹些代碼(註:消息處理代碼)。WndProc函數是負責處理到達該窗口的每個消息。所以我們將在這個函數添加壹個另外的case分支:
case DOT_NET_BUTTON_PRESSED:
MessageBox(NULL, TEXT("Message from C# arrived: Button Pressed!!"),
szTitle, MB_OK | MB_ICONINFORMATION);
break;
滑輪組:F = G總ns =nh/頁對於定滑輪而言:∵ n= ∴