Original: http://www.cnblogs.com/clever101/archive/2009/12/14/1624204.html
This article is transferred from the blog garden, this article author according to codeproject English version translation!
Original address: Http://www.codeproject.com/KB/cs/DotNetActiveX.aspx
Original Daniel Yanovsky
Translation: Zhu Jinchan
This article describes how to integrate C # windows (Window Form) in C + + engineering.
Introduced
This article describes how to integrate a C # form (also known as a Windows Form) in a C + + window. Our C + + window is created using purely WIN32 API functions and does not require MFC.
Background
In general, using C # for Windows programming is much easier than using C + +, especially when we cannot use the MFC library. So many programmers like to use the C # environment to build their projects. But sometimes it needs to be programmed in a C + + environment. For example, when you want to develop an attachment software on a C + + program. The way we solve this problem is to develop a plugin for it, such as the famous notepad++ program. Notepad++ is written in the C + + language, using purely WIN32 API functions and STL to ensure that it has a high speed and a smaller executable file. But the disadvantage is that the design and development of the interface becomes difficult. So we faced this challenge and decided to use C # to create our plugins. How we successfully integrate our. NET plugin to a window created using purely WIN32 API functions? I hope that this article will help you understand this.
We have prepared a small example for this article. If you would like to see the full source of the plugin we have written for notepad++, please visit our plugin homepage.
Creating ActiveX Control basics with the C # language
This part of the solution is based on the article "Exposing Windows Forms controls in the form of ActiveX Controls", published by Morgan Skinner. Although Skinner offered his solution to be developed by the visual Studio 8 Beta, his example runs very well (only minor changes) in the VS8 release. Here is a list of the changes we made to the Skinner solution:
1.
Set upClassInterface
为ClassInterfaceType.None(这样我们暴露唯一指定的接口到COM)。更多的信息在下一章。
2. The project should be set as visible to COM. to do this:assembly information on the Application tab on the Project Properties menu item Make assembly COM visible on the dialog boxis selected (specifically):
1. You should also register the project for COM interop (note that in VS8.0 's release, the "Build" Property window and the beta version have different designs). When this feature is selected, Visual Studio automatically registers. NET ActiveX controls when the project is successfully compiled. (as specific)
4.
In Skinner's article, theComUnregisterFunction()
函数中有一个小错误。下面是正确的函数:
<summary>
Unregister ActiveX DLL function
</summary>
<param name= "I_key" ></param>
[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 a 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 ();
}
Exporting a clear method for COM
For a more precise design, we export the specified method for COM. Every external program that uses our controls will only access the methods they need.
The best way to export a concrete method is to create an interface for all related methods. Then, the specific attributes should be added to this interface. The form class should be implemented on this interface.
<summary>
COM Interface-enables to run C # code from C + +
</summary>
[InterfaceType (Cominterfacetype.interfaceisdual)]
public interface Icssexplorerinterface
{
void Setbuttoncaption (String strnewcaption);
void Setadapterdllptr (IntPtr i_adapterdllptr);
}
Using the Microsoft messaging System
We use the Microsoft Messaging System and VC engineering container windows and other windows to communicate. We do not handle any events, because if so it will become more complex and it is not required for our solution.
Let's add the following code to ourMyDotNetActiveX
类去允许消息传输:
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
In the initialization code, we assume that our container window will be passed to its window handle ( hwnd
参数
) to communicate.
Compiling the project
Now we are ready to compile and test this control. Visual Studio automatically registers our ActiveX controls after a successful compilation. You can view the registration information through the free software Regdllview.
To test the control in the ActiveX Control Test Container
Before we jump to the next step in this article, it's a good time to test our controls in a third-party program. We tested using the ActiveX Control Test Container (tstcon32.exe). This program can be found in the installation directory of Visual Studio.
1. Insert a control from theInsert New controlmenu item of theEditmenu bar
1. Now select theInvoke Methodsmenu item in theControlmenu bar.
2. in the method Name Combo-box control, select the setbuttoncaption function.
3. Enter "Hello" in the Parameter Value text box and press the "Invoke" button
5. Here are the test results
Add C # ActiveX controls to the C + + window using the ATL control containment
Any ActiveX control can be included by using the active Template Library (ATL).
In this section of the guide, we will complete the following work:
- Create a C + + WIN32 Application project
- Inserting our ActiveX control into the C + + window
- Send command to ActiveX control
- Receive a message from our ActiveX control
Create a C + + WIN32 Application project
- Create a new Win32 project and name it "
CPP_Container
:":
2. Use the default settings and press the "OK" button:
Inserting a C # ActiveX control into a C + + window
- Add the following code to the beginning of 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. When Visual Studio compiles our C # project, it creates the dotnetactivex.tlb file. This file contains all the methods and structures of the project. We will import the data using the following command:
Import C # control function and structures
#import "Dotnetactivex.tlb" Named_guids raw_interfaces_only
3. Add the following function to Cpp_container.cpp. This function inserts an ATL container into the window and loads our C # ActiveX controls:
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);
::D Estroywindow (_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. For more accurate development, add the following code in the WndProc function to the Wm_destory message processing block (note: In order to destroy the C # ActiveX control window and free the loaded memory).
_pdotnetcomptr->release ();
::D Estroywindow (_hatl);
_punk->release ();
:: FreeLibrary (_hweblib);
5. Finally, in_tWinMain
函数调用loadActiveX函数。
Loadactivex (TEXT ("Dotnetactivex.mydotnetactivex"));
Send command to C # ActiveX control
After inserting the TLB file, all of the methods that we exported in the C # project will be displayed. Now we simply call the relevant method:
Char *strhelloworld = "Hello world!";
_bstr_t Bstrhelloworld (Strhelloworld);
_pdotnetcomptr->setbuttoncaption (Bstrhelloworld);
This will change the title of the button to "Hello world!".
Receiving messages from C # ActiveX controls
Messages from the C # control through Microsoft's messaging system can reach the C + + window. By invoking a loadActiveX
函数我们已经将我们的窗口句柄发给
C # control. So now, we just need to add some code to the WndProc function (note: Message processing code). WndProc
函数是负责处理到达该窗口的每个消息。所以我们将在这个函数添加一个另外的case分支:
Case dot_net_button_pressed:
MessageBox (NULL, TEXT ("Message from C # Arrived:button pressed!!"),
SzTitle, MB_OK | Mb_iconinformation);
Break
Now, you can press the button in the C # ActiveX control and see the following result:
Conclusion
We hope this article will help developers who mix the C # and C + + languages in their projects. You can view the notepad++ plugin we developed: Cssexplorer plug-in for notepad++. All of the ideas mentioned above have been implemented in our plugins.
Reference documents
- C # programming Guide-example COM Class (C # Programming Guide)
- How to add an ATL control containment support to any windows in Visual C + +
- Exposing Windows Forms controls as ActiveX Controls
History
- October 10, 2009: initial submission
Authorized
This article, including all relevant source code and files, follows the code Project Open License (CPOL) licensing agreement.
About the author
Daniel Yanovsky: Career: Software development engineer. Nationality: Israel.
"Reprint" How to integrate C # window in C + + engineering