C#調用C++的DLL,並將其返回的Form.Handle嵌入C#的Control中顯示


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)
{
}

24 个解决方案

#1


請高手花點時間看看呀,着急用啊!

#2


不會吧,沒高手進來看看,我頂,大家幫頂啊

#3


沒試過,頂

#4


謝謝你頂,再頂上去,我就不信沒人會的

#5


建議LZ換個思路:C#調用C++的dll(功能),可以采取互操作的辦法;如果要調C++的界面,最好不要通過dll的方式new出來。原因:
1、C++與C#的界面風格完全不同,強制揉合在一起,很別扭;
2、兩者響應事件,改變窗體背景、大小、字體、顏色等方式都不相同;

解決辦法:
1、用C#重寫C++界面;
2、把C++界面封裝成獨立的exe供C#進程調用

#6


引用 5 樓 sdl2005lyx 的回復:
建議LZ換個思路:C#調用C++的dll(功能),可以采取互操作的辦法;如果要調C++的界面,最好不要通過dll的方式new出來。原因:
1、C++與C#的界面風格完全不同,強制揉合在一起,很別扭;
2、兩者響應事件,改變窗體背景、大小、字體、顏色等方式都不相同;

解決辦法:
1、用C#重寫C++界面;
2、把C++界面封裝成獨立的exe供C#進程調用

關鍵是這2條路都不行啊,公司C++的DLL是已經存在的,上千支DLL不可能全轉為EXE,也不可能C#重寫
畫面別扭客戶能接受,現在就要求能在C#中嵌入C++的Form,並且不當機就行了

#7


“關鍵是這2條路都不行啊,公司C++的DLL是已經存在的”,
我也遇到類似問題,為什么不能封裝成獨立的EXE呢?

VC寫的含Form的dll,無非就兩者方式,要么就是Dialog,要么就是CView方式,
前者封裝很簡單,后者要復雜些。

#8


給你我做過的例子:
在原VC的dll庫里加函數

1、Dialog模式的
   void OpenVQCSet() //VQC限值設置
{
CVQCLmtSet pVQCSet;    //這個是對話框
pVQCSet.DoModal();
}

當然建立新建Dialog的EXE工程://注意我屏蔽的代碼

BOOL CVQCLimitApp::InitInstance()
{
AfxEnableControlContainer();

// Standard initialization
// If you are not using these features and wish to reduce the size
//  of your final executable, you should remove from the following
//  the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

OpenVQCSet();           //這里調用原dll里的對話框,后面的代碼屏蔽,就只顯示這個對話框
//  CVQCLimitDlg dlg;
//  m_pMainWnd = &dlg;
//  int nResponse = dlg.DoModal();
//  if (nResponse == IDOK)
//  {
//  // TODO: Place code here to handle when the dialog is
//  //  dismissed with OK
//  }
//  else if (nResponse == IDCANCEL)
//  {
//  // TODO: Place code here to handle when the dialog is
//  //  dismissed with Cancel
//  }

// Since the dialog has been closed, return FALSE so that we exit the
//  application, rather than start the application's message pump.
return FALSE;
}





#9


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
}

#10


幾個dll改過來還可以,關鍵現在公司C++的DLL有上千支,
並且用Dll方式時,是可以實現資料庫連接共享的
做成獨立exe的話,每運行一個exe都要產生獨自的資料庫連接
最主要的原因還是DLL太多,並且DLL不歸我管,沒有說的過去的理由不可能讓別人改DLL

#11


你還是沒理解我的意思,針對你來說,你只是用幾個dll,你也可以不用改變原來的dll,直接在exe工程里,加入那幾行代碼而已!

#12


其實,我也正在做C#改造VC界面的事情,一般的原則:
1、對於較簡單的界面,如登錄,簡單對話框,數據框等,盡量用C#Form重寫,以便風格統一。
2、對於比較復雜的界面,特殊是這些工程有涉及到比較復雜的算法,在C#這邊一時難以改造,而且有可能對原來的算法或流程理解錯誤,那只有退而求其次,把少數的dll封裝成獨立的dll,在C#這邊進程調用,難看點而已!



不知LZ是否也是在做類似的事情。。。。

#13


你的意思是用C++寫個exe,通過傳入參數的方式調用不同的dll顯示C++的Form。
然后在C#中用Process調用這個exe?

#14


這樣是可以,但資料庫連接等就得每個Process都是獨立的了。

#15


是的,這樣改造工作最少,原來的流程、算法都能沿用。。。

#16


引用 15 樓 sdl2005lyx 的回復:
是的,這樣改造工作最少,原來的流程、算法都能沿用。。。


如果我就想用DLL,你能有解決方案嗎?那些個領導會覺得:每個Process的數據庫連接不能共享肯定不好的

#17


sdl2005lyx, 奇怪了,我自己用CB創建的exe竟然不能嵌入C#的Form。而換成notepad.exe就行

難道exe文件也有特殊需求才行?

#18


要強行從底層修改一個組件,應該先搞懂原理上是否真的可行再說。強行破壞一個組件,你倒是顯示了,不是也宕機了嘛。

你的領導有沒有承受“經過論證發覺完全錯誤”的心理准備呢?如果沒有,我建議你趕緊退,把c++的界面處理程序掃到字紙簍里。

#19


把這個組件寫成com。

#20


用C++的ATL寫成COM組件的形式,這樣就好調用了,直接把COM包引進來就能直接用了,僅供參考。

#21


不用COM 也可以,平台調用P-INVOKE   參考
http://tech.ddvip.com/2010-09/1283763341159872_2.html

#22


找到原因了,原來是因為:
DllInvoke _DllInvoke = new DllInvoke(Application.StartupPath + @"\Project.dll");

_DllInvoke應該定義為全局變量,否則離開這個函數就會被GC回收,而
DllInvoke 的析構中又FreeLibrary了,當然會當機

#23


解決就好。。。^_^

#24


mark

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com