关于Windows API FindWindow() 偶尔找不到相应窗口的现象及我的解决方案

项目场景:

我的解决方案下有两个项目,一个是C++的MFC项目,一个是C#的WPF项目。

出于工作需要我要在C++项目中打开C#项目的exe文件,然后在C++端找到该exe的主窗口句柄,从而利用SendMessage()将C++的消息发送到该exe。


问题描述:

毫无疑问,当需要找窗口句柄时,首先想到的自然是FindWindow(),这个函数能够在系统中帮我们找到所需窗口的句柄。该函数的参数有两个,第一个是所找窗口的类名,第二个是所找窗口的名字。一般来说,第一个参数可以填NULL,但是第二个参数一般不要省略。

但是,当我生成项目的exe放到Surface上面,然后在Surface低电量(大约10%)重开机运行时,发现消息竟然发送不出去!debug发现,是FindWindow()这个API在作怪!查询网上相关资料,发现也有人说在重开机后FindWindow找不到相应窗口的概率会变大,但是又找不到解决方法。于是经过一天的摸索,针对我的项目,我给出如下解决方案:

直接在打开exe文件时将C++的窗口句柄传过去,然后在C#端接收获得C++的窗口句柄,再在C#端利用C++的窗口句柄将C#的主窗口句柄回传给C++!


解决方案:

  1. MFC端:利用ShellExecuteA()函数打开WPF生成的exe文件,同时将自己当前的窗口句柄作为命令行参数传递过去。代码如下:
//Dlg.cpp
	//Send the HWND to C# program
	std::string hwnd = std::to_string((WPARAM)this->GetSafeHwnd());	//这里利用WPARAM将窗口句柄转化一次之后再使用to_string()将句柄转化为string
	printf("%s\n", hwnd.c_str());
	ShellExecuteA(this->GetSafeHwnd(), "open", exePath + "PalmServer.exe", hwnd.c_str(), NULL, 1);
  1. WPF端:将获得的命令行参数解析出C++端的窗口句柄,然后将自己的窗口句柄发送给C++端
//App.xml
<Application x:Class="PalmServer.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:PalmServer"
             Startup="Application_Startup"	//定义程序入口
             >
    <Application.Resources>
        
    </Application.Resources>
</Application>
//App.xml.cs
    public partial class App : Application
    {
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern int SendMessage(IntPtr hwnd, uint wMsg, IntPtr wParam, int lParam);

        // Send C++ program the C# mainwindow HWND
        private const uint WM_PS_HWND = 9034;     // Tell client the HWND of this window

		//程序入口
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            MainWindow wnd = new MainWindow();
            wnd.Show();

            if (e.Args.Length == 1)
            {
                int a = Convert.ToInt32(e.Args[0]);                     //将包含C++的窗口句柄的string转化为int
                IntPtr proxyHandleByShellExec = (IntPtr)a;              //MFC中的HWND类型等同于WPF中的IntPtr类型
                Console.WriteLine(proxyHandleByShellExec.ToString());   //输出看看句柄的数字是什么    

                if (proxyHandleByShellExec != (IntPtr)0)
                {
                    var mainWindowHandle = new System.Windows.Interop.WindowInteropHelper(wnd).Handle;  //C#获得窗口句柄
                    Console.WriteLine(mainWindowHandle.ToString());
                    SendMessage(proxyHandleByShellExec, WM_PS_HWND, mainWindowHandle, 0);               //将MainWindow的窗口句柄发送给C++端
                }
            }
            else
                Console.WriteLine(e.Args.Length.ToString());
            }
        }

    }
  1. MFC:定义消息处理函数,接收消息即可。
//Dlg.h
	// PalmServer HWND
	class CDlgPalmProbe : public CDialogEx
	{
	private:
		HWND hProbeServerBySendMessage;
		afx_msg LRESULT OnPalmServerHWND(WPARAM wParam, LPARAM lParam);	//消息处理函数
	}
//Dlg.cpp
// PalmServer HWND
#define WM_PS_HWND					9034	  //定义消息

BEGIN_MESSAGE_MAP(CDlgPalmProbe, CDialogEx)
	ON_MESSAGE(WM_PS_HWND, &CDlgPalmProbe::OnPalmServerHWND)	//绑定消息和消息处理函数
END_MESSAGE_MAP()

//Receive the PalmProbe HWND from C# program
LRESULT CDlgPalmProbe::OnPalmServerHWND(WPARAM wParam, LPARAM lParam)
{
	hProbeServerBySendMessage = (HWND)wParam;
	long a = (WPARAM)hProbeServerBySendMessage;
	_cprintf("C# HWND: %ld\n", a);
	return 0;
}

# 总结 除了这种方法,我也试过利用文本中介写入句柄再去读取的方法。但是读写方法可能会受制于权限,所以不推荐这种方法。读写注册表倒是一个相对好一点的办法,但是我没试过。总之,相较于读写,这种直接通过消息处理的方法还是更方便快捷,而且经过测试确实也能解决上述的问题,值得大家去试一试的。有什么问题或想法欢迎在评论提出,我看到会及时回复的!谢谢!
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值