由于目前要做一个Unity的项目,在Win7系统上,要实现全屏功能,并且有聊天功能。做过的码农都知道,Win7系统上,全屏时输入法看不到选词。针对这个问题,我在网上google了一下,也结合之前做的东西,总算解决了这个问题。
其实道理很简单,既然全屏不能看到选词,那么就不全屏。但是窗口要铺满屏幕,并且将边框隐藏。
这个分两步走,我们直接贴代码,代码里有注释。首先是C#端:
using UnityEngine;
using System.Runtime.InteropServices;
using System;
using System.Collections;
public class SetUnityWindow : MonoBehaviour
{
[DllImport("UnityWindowSet")]
static extern MONITORRECT GetMonitorRect();
[DllImport("UnityWindowSet")]
static extern int SetWindowFullScreen(MONITORRECT rect);
//这个属性告诉编译器,当把该结构传递给非托管编译的时候,按照我们定义的顺序排序,而不是单字节对齐或者怎样。
[StructLayout(LayoutKind.Sequential)]
struct MONITORRECT
{
public long left;
public long top;
public long right;
public long bottom;
}
Vector2 screenRect;
IEnumerator Start()
{
yield return new WaitForEndOfFrame();
MONITORRECT monitorRect = GetMonitorRect();
screenRect.x = monitorRect.right - monitorRect.left;
screenRect.y = monitorRect.bottom - monitorRect.top;
SetWindowFullScreen(monitorRect);
}
}
然后是C++端代码:
#include<Windows.h>
#include<vector>
using namespace std;
struct ALLMONITORINFO
{
HMONITOR hMonitor;
RECT rect;
bool isPrimary;
};
//获取所有显示器信息的回调函数。
static BOOL CALLBACK MonitorEnumProc(__in HMONITOR hMonitor, __in HDC hdcMonitor, __in LPRECT lprcMonitor, __in LPARAM dwData)
{
vector<ALLMONITORINFO>& infoArray = *reinterpret_cast<vector<ALLMONITORINFO>* >(dwData);
ALLMONITORINFO monitorInfo;
monitorInfo.hMonitor = hMonitor;
monitorInfo.rect = *lprcMonitor;
HMONITOR priMonitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
if (priMonitor == hMonitor)
monitorInfo.isPrimary = true;
else
monitorInfo.isPrimary = false;
infoArray.push_back(monitorInfo);
return TRUE;
}
extern "C"
{
__declspec(dllexport) RECT __stdcall GetMonitorRect()
{
//获取当前前景窗口的句柄。这句话很重要,获取到窗口句柄是我们后面所有工作的基础。只要我们会C++ Winform编程,就可以修改unity的窗口显示了。
HWND hWin = GetForegroundWindow();
HMONITOR hMonitor = MonitorFromWindow(hWin, MONITOR_DEFAULTTONEAREST);
vector<ALLMONITORINFO> mInfo;
mInfo.clear();
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, reinterpret_cast<LPARAM>(&mInfo));
for (int i = 0; i < mInfo.size(); i++)
{
if (hMonitor == mInfo[i].hMonitor)
{
return mInfo[i].rect;
}
}
return RECT();
}
__declspec(dllexport) BOOL __stdcall SetWindowFullScreen(RECT rect)
{
HWND hWindow = GetForegroundWindow();
SetWindowLong(hWindow, GWL_STYLE, WS_POPUP);
SetWindowPos(hWindow, 0, 0, 0, (int)(rect.right - rect.left), (int)(rect.bottom - rect.top), SWP_SHOWWINDOW);
return TRUE;
}
}
其实在这个例子中,在C#端,我们可以精简到只调用一个C++函数。但是有一个现象,那就是我们自己定义的RECT结构竟然能跟C++的RECT结构互相使用。因为C++开发做得比较少,所以对这个现象比较吃惊。所以以后有类似情况时,我们可以按照C++的方式重新定义一个结构,只要字节对齐就可以了。
然后我们能修改Unity窗口的基础是GetForegroundWindow()函数,即获取当前前景窗口的句柄。然后如果有C++ Winform编程基础的话,就可以修改Unity窗口变成任何自己想要的样式了。这个函数也可以通过将user32.dll放到Unity的Plugins文件夹下,然后在C#中调用。
我们用C++写的这个dll有一个问题,那就是32位和64位的问题。一直想找个办法让一个dll既能兼容32位,又能兼容64位,就像user32.dll一样。但是无果。