C#调用C++的DLL,并将其返回的Form.Handle嵌入C#的Control中显示

zmy0611 2011-07-21 03:27:27
C#写的Form,要求能调用C++的DLL(API), 在API中创建一个C++的Form,返回C++.Form.Handle嵌入到C#的Form中显示
目前已能显示,但只要改变C#Form的Size,就当机,高手看看哪里写错了:
C#代码如下:

/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private ApplicationControl applicationControl1;
private IContainer components;
public Form1()
{
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code private void InitializeComponent()
{
this.applicationControl1 = new ApplicationControl();
this.SuspendLayout();
//
// applicationControl1
//
this.applicationControl1.Dock = System.Windows.Forms.DockStyle.Fill;
this.applicationControl1.Location = new System.Drawing.Point(0, 0);
this.applicationControl1.Name = "applicationControl1";
this.applicationControl1.Size = new System.Drawing.Size(447, 328);
this.applicationControl1.TabIndex = 0;
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(447, 328);
this.Controls.Add(this.applicationControl1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
}
public class ApplicationControl : System.Windows.Forms.Panel
{
bool created = false;
IntPtr appWin;
public ApplicationControl()
{
}
[DllImport("user32.dll", EntryPoint="GetWindowThreadProcessId", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)]
private static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);
[DllImport("user32.dll", SetLastError=true)]
private static extern IntPtr FindWindow (string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError=true)]
private static extern long SetParent (IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint="GetWindowLongA", SetLastError=true)]
private static extern long GetWindowLong (IntPtr hwnd, int nIndex);
[DllImport("user32.dll", EntryPoint="SetWindowLongA", SetLastError=true)]
private static extern long SetWindowLong (IntPtr hwnd, int nIndex, long dwNewLong);
[DllImport("user32.dll", SetLastError=true)]
private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);
[DllImport("user32.dll", SetLastError=true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32.dll", EntryPoint="PostMessageA", SetLastError=true)]
private static extern bool PostMessage(IntPtr hwnd, uint Msg, long wParam, long lParam);
private const int SWP_NOOWNERZORDER = 0x200;
private const int SWP_NOREDRAW = 0x8;
private const int SWP_NOZORDER = 0x4;
private const int SWP_SHOWWINDOW = 0x0040;
private const int WS_EX_MDICHILD = 0x40;
private const int SWP_FRAMECHANGED = 0x20;
private const int SWP_NOACTIVATE = 0x10;
private const int SWP_ASYNCWINDOWPOS = 0x4000;
private const int SWP_NOMOVE = 0x2;
private const int SWP_NOSIZE = 0x1;
private const int GWL_STYLE = (-16);
private const int WS_VISIBLE = 0x10000000;
private const int WM_CLOSE = 0x10;
private const int WS_CHILD = 0x40000000;
/// <summary>
/// 控件尺寸改变时重绘
/// </summary>
/// <param name="e">Not used</param>
protected override void OnSizeChanged(EventArgs e)
{
this.Invalidate();
base.OnSizeChanged(e);
}
/// <summary>
/// Creeate control when visibility changes
/// </summary>
/// <param name="e">Not used</param>
protected override void OnVisibleChanged(EventArgs e)
{
if (!created)
{
created = true;
appWin = IntPtr.Zero;
try
{
//调用DLL的API并嵌入C#的Form
DllInvoke _DllInvoke = new DllInvoke(Application.StartupPath + @"\Project.dll");
TestCallDelegate TestCall = (TestCallDelegate)_DllInvoke.Invoke("TestCall", typeof(TestCallDelegate));
appWin = TestCall();
/* 调用Exe并嵌入C#的Form方式
Process p = System.Diagnostics.Process.Start("Exe路径");
p.WaitForInputIdle();
appWin = p.MainWindowHandle;*/
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Error");
}
// Put it into this form
SetParent(appWin, this.Handle);
// Remove border and whatnot
SetWindowLong(appWin, GWL_STYLE, WS_VISIBLE);
// Move the window to overlay it on this window
MoveWindow(appWin, 0, 0, this.Width, this.Height, true);
}
base.OnVisibleChanged (e);
}
/// <summary>
/// 控件销毁时关闭C++的FORM
/// </summary>
/// <param name="e"></param>
protected override void OnHandleDestroyed(EventArgs e)
{
// Stop the application
if (appWin != IntPtr.Zero)
{
// Post a colse message
PostMessage(appWin, WM_CLOSE, 0, 0);
System.Threading.Thread.Sleep(1000);
// Clear internal handle
appWin = IntPtr.Zero;
}
base.OnHandleDestroyed (e);
}
/// <summary>
/// Update display of the executable
/// </summary>
/// <param name="e">Not used</param>
protected override void OnResize(EventArgs e)
{
if (this.appWin != IntPtr.Zero)
{
MoveWindow(appWin, 0, 0, this.Width, this.Height, true);
}
base.OnResize (e);
}
}
public delegate IntPtr TestCallDelegate();
/// <summary>
/// 动态调用Dll
/// </summary>
public class DllInvoke
{
[DllImport("kernel32.dll")]
private extern static IntPtr LoadLibrary(string path);
[DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr lib, string funcName);
[DllImport("kernel32.dll")]
private extern static bool FreeLibrary(IntPtr lib);

private IntPtr hLib;
public DllInvoke(String DLLPath)
{
hLib = LoadLibrary(DLLPath);
}
~DllInvoke()
{
FreeLibrary(hLib);
}
/// <summary>
/// 将要执行的函数转换为委托
/// </summary>
/// <param name="APIName"></param>
/// <param name="t"></param>
/// <returns></returns>
public Delegate Invoke(string APIName, Type t)
{
IntPtr api = GetProcAddress(hLib, APIName);
return (Delegate)Marshal.GetDelegateForFunctionPointer(api, t);
}
}


C++代码如下:(生成Project.Dll, 放在C#项目的Debug下供调用)

Unit1.h:
class TForm1 : public TForm
{
__published:
private:
public:
};

Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
extern "C" __declspec(dllexport) WINAPI HWND TestCall()
{
TForm1 *f = new TForm1(NULL);
f->Visible = true;
return f->Handle;
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}

...全文
654 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
zmy0611 2011-07-25
  • 打赏
  • 举报
回复
sdl2005lyx, 奇怪了,我自己用CB创建的exe竟然不能嵌入C#的Form。而换成notepad.exe就行

难道exe文件也有特殊需求才行?
zmy0611 2011-07-25
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 sdl2005lyx 的回复:]

是的,这样改造工作最少,原来的流程、算法都能沿用。。。
[/Quote]

如果我就想用DLL,你能有解决方案吗?那些个领导会觉得:每个Process的数据库连接不能共享肯定不好的
sdl2005lyx 2011-07-25
  • 打赏
  • 举报
回复
是的,这样改造工作最少,原来的流程、算法都能沿用。。。
feng9330 2011-07-25
  • 打赏
  • 举报
回复
mark
sdl2005lyx 2011-07-25
  • 打赏
  • 举报
回复
解决就好。。。^_^
zmy0611 2011-07-25
  • 打赏
  • 举报
回复
找到原因了,原来是因为:
DllInvoke _DllInvoke = new DllInvoke(Application.StartupPath + @"\Project.dll");

_DllInvoke应该定义为全局变量,否则离开这个函数就会被GC回收,而
DllInvoke 的析构中又FreeLibrary了,当然会当机
jian03112 2011-07-25
  • 打赏
  • 举报
回复
不用COM 也可以,平台调用P-INVOKE 参考
http://tech.ddvip.com/2010-09/1283763341159872_2.html
jian03112 2011-07-25
  • 打赏
  • 举报
回复
用C++的ATL写成COM组件的形式,这样就好调用了,直接把COM包引进来就能直接用了,仅供参考。
卧_槽 2011-07-25
  • 打赏
  • 举报
回复
把这个组件写成com。
  • 打赏
  • 举报
回复
要强行从底层修改一个组件,应该先搞懂原理上是否真的可行再说。强行破坏一个组件,你倒是显示了,不是也宕机了嘛。

你的领导有没有承受“经过论证发觉完全错误”的心理准备呢?如果没有,我建议你赶紧退,把c++的界面处理程序扫到字纸篓里。
sdl2005lyx 2011-07-22
  • 打赏
  • 举报
回复
建议LZ换个思路:C#调用C++的dll(功能),可以采取互操作的办法;如果要调C++的界面,最好不要通过dll的方式new出来。原因:
1、C++与C#的界面风格完全不同,强制揉合在一起,很别扭;
2、两者响应事件,改变窗体背景、大小、字体、颜色等方式都不相同;

解决办法:
1、用C#重写C++界面;
2、把C++界面封装成独立的exe供C#进程调用

zmy0611 2011-07-22
  • 打赏
  • 举报
回复
谢谢你顶,再顶上去,我就不信没人会的
confidenceyu 2011-07-22
  • 打赏
  • 举报
回复
没试过,顶
zmy0611 2011-07-22
  • 打赏
  • 举报
回复
不会吧,没高手进来看看,我顶,大家帮顶啊
zmy0611 2011-07-22
  • 打赏
  • 举报
回复
这样是可以,但资料库连接等就得每个Process都是独立的了。
zmy0611 2011-07-22
  • 打赏
  • 举报
回复
你的意思是用C++写个exe,通过传入参数的方式调用不同的dll显示C++的Form。
然后在C#中用Process调用这个exe?
sdl2005lyx 2011-07-22
  • 打赏
  • 举报
回复
其实,我也正在做C#改造VC界面的事情,一般的原则:
1、对于较简单的界面,如登录,简单对话框,数据框等,尽量用C#Form重写,以便风格统一。
2、对于比较复杂的界面,特殊是这些工程有涉及到比较复杂的算法,在C#这边一时难以改造,而且有可能对原来的算法或流程理解错误,那只有退而求其次,把少数的dll封装成独立的dll,在C#这边进程调用,难看点而已!



不知LZ是否也是在做类似的事情。。。。
sdl2005lyx 2011-07-22
  • 打赏
  • 举报
回复
你还是没理解我的意思,针对你来说,你只是用几个dll,你也可以不用改变原来的dll,直接在exe工程里,加入那几行代码而已!
zmy0611 2011-07-22
  • 打赏
  • 举报
回复
几个dll改过来还可以,关键现在公司C++的DLL有上千支,
并且用Dll方式时,是可以实现资料库连接共享的
做成独立exe的话,每运行一个exe都要产生独自的资料库连接
最主要的原因还是DLL太多,并且DLL不归我管,没有说的过去的理由不可能让别人改DLL
sdl2005lyx 2011-07-22
  • 打赏
  • 举报
回复
2、CView模式
这个要复杂些:
void OpenVQCRun(CWnd* pWnd) //注意:这里比上面多个参数
{
CRuntimeClass *pViewClass=RUNTIME_CLASS(CVQCRunFrmView );
CCreateContext *pContext=new CCreateContext;
pContext->m_pCurrentDoc=NULL;
pContext->m_pCurrentFrame=NULL;
pContext->m_pLastView=NULL;
pContext->m_pNewDocTemplate=NULL;
pContext->m_pNewViewClass=pViewClass;

CWnd *pTempWnd=DYNAMIC_DOWNCAST(CWnd,pViewClass->CreateObject());
pTempWnd->Create(NULL,NULL,WS_CHILD ,CRect(0,0,725,660),
pWnd,IDD_VQC_RUN_VIEW,pContext); //注意这里的View大小要跟你实际大小一样
delete pContext;
CView* pView=DYNAMIC_DOWNCAST(CVQCRunFrmView ,pTempWnd);
pView->OnInitialUpdate();
pView->ShowWindow(SW_SHOW);
}

同样再建立新的Dialog的工程:


BOOL CVQCRunDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// Add "About..." menu item to system menu.

// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}

// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon

// TODO: Add extra initialization here
OpenVQCRun(this); //注意,函数的位置跟上面的不一样
return TRUE; // return TRUE unless you set the focus to a control
}
加载更多回复(4)

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧