Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

问题背景:

现在在搞PC端应用开发,我们开发中需要调用系统的窗口以及需要最大化最小化,缩放窗口拖拽窗口,以及设置窗口位置,去边框等功能

解决根据:

使用user32.dll解决

具体功能:

Unity中对Windows窗口设置

<1>.unity中调用打开文件窗口和保存窗口:

调用Comdlg32.dll中方法

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks; namespace Tx3d.Ventilation
{
/// <summary>
/// 场景窗口类型基类
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class SceneWindows
{
public int structSize = ;
public IntPtr dlgOwner = IntPtr.Zero;
public IntPtr instance = IntPtr.Zero;
public String filter = null;
public String customFilter = null;
public int maxCustFilter = ;
public int filterIndex = ;
public String file = null;
public int maxFile = ;
public String fileTitle = null;
public int maxFileTitle = ;
public String initialDir = null;
public String title = null;
public int flags = ;
public short fileOffset = ;
public short fileExtension = ;
public String defExt = null;
public IntPtr custData = IntPtr.Zero;
public IntPtr hook = IntPtr.Zero;
public String templateName = null;
public IntPtr reservedPtr = IntPtr.Zero;
public int reservedInt = ;
public int flagsEx = ;
} [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class OpenFile : SceneWindows
{ } public class OpenFileWindow
{
[DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)] public static extern bool GetOpenFileName([In, Out] OpenFile ofd);
} public class SaveFileWindow
{
[DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
public static extern bool GetSaveFileName([In, Out] SaveFile ofd);
} [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class SaveFile : SceneWindows
{ } }

具体使用:

  /// <summary>
/// 创建保存场景/另存场景面板
/// </summary>
/// <param name="titleName">面板主题名</param>
/// <param name="filePath">保存路径</param>
private void SaveOrSaveAsWindows(string titleName,Action<string> action)
{
SaveFile pth = new SaveFile();
pth.structSize = System.Runtime.InteropServices.Marshal.SizeOf(pth);
pth.filter = "All Files\0 *.*\0\0";//是什么文件类型就修改此处
pth.file = new string(new char[]);
pth.maxFile = pth.file.Length;
pth.fileTitle = new string(new char[]);
pth.maxFileTitle = pth.fileTitle.Length;
pth.initialDir = Application.dataPath; // default path
pth.title = titleName;
pth.defExt = "json";
pth.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008;
if (SaveFileWindow.GetSaveFileName(pth))
{
string filepath = pth.file;//选择的文件路径;
action(filepath);
}
} /// <summary>
/// 打开项目弹框
/// </summary>
/// <returns></returns>
private void OpenWindow(Action<string> action)
{
string filepath = "";
OpenFile pth = new OpenFile();
pth.structSize = System.Runtime.InteropServices.Marshal.SizeOf(pth);
// pth.filter = "JSON file(*.json)";//是什么文件类型就修改此处
pth.filter = "All Files\0*.*\0\0";
pth.file = new string(new char[]);
pth.maxFile = pth.file.Length;
pth.fileTitle = new string(new char[]);
pth.maxFileTitle = pth.fileTitle.Length;
pth.initialDir = Application.dataPath; // default path
pth.title = "打开项目";
pth.defExt = "json"; //注意 一下项目不一定要全选 但是0x00000008项不要缺少
//0x00080000 是否使用新版文件选择窗口,0x00000200 是否可以多选文件
pth.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008; if (OpenFileWindow.GetOpenFileName(pth))
{
filepath = pth.file;//选择的文件路径;
action(filepath);
}
} /// <summary>
/// 根据路径生成本地json文件
/// </summary>
/// <param name="filePath">写入的路径</param>
/// <param name="jsonData">写入的json数据</param>
private void WriteJsonFile(string filePath,JsonData jsonData)
{
//构建文件流,创建文件,若存在则覆盖
FileStream fileStream = new FileStream(filePath, FileMode.Create); //构建写流,设置文件格式
StreamWriter sw = new StreamWriter(fileStream, Encoding.UTF8); //ToJson接口将你的json数据传进去,并自动转换为string类型
string json = JsonMapper.ToJson(jsonData); //将转好的json字符串写入文件
sw.WriteLine(json); sw.Flush(); //关流,释放资源
sw.Close();
fileStream.Close();
sw.Dispose();
} /// <summary>
/// 读取json文件
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private JsonData ReadJson(string path)
{
//构建读流,设置文件格式
StreamReader sr = new StreamReader(path); //再转换成json数据
JsonReader json = new JsonReader(sr);
//读取json数据
JsonData data = JsonMapper.ToObject(json); sr.Close(); return data;
} /// <summary>
/// 获取SceneName
/// </summary>
/// <param name="name">路径名字</param>
/// <returns></returns>
private string GetSceneName(string name)
{
string [] names= name.Split('\\');
string myname=names[names.Length - ].Split('.')[];
return myname;
}

效果:

打开:

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

保存:

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

<2>.去边框/设置窗口尺寸和位置

导入user32.dll的相关方法:

         //设置窗口边框
[DllImport("user32.dll")]
public static extern IntPtr SetWindowLong(IntPtr hwnd, int _nIndex, int dwNewLong); //设置窗口位置,尺寸
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

具体使用:

 //边框参数
private const uint SWP_SHOWWINDOW = 0x0040;
private const int GWL_STYLE = -;
private const int WS_BORDER = ; //隐藏标题栏图标
private const int WS_POPUP = 0x800000;
private const int WS_SYSMENU = 0x80000; //最大最小化
private const int SW_SHOWMINIMIZED = ;//(最小化窗口)
private const int SW_SHOWMAXIMIZED = ;//最大化窗口 //去除标题栏保留边框
private const int WS_CAPTION = 0x00C00000;
private const int WS_THICKFRAME = 0x00040000; //去除上边栏(不可拖拽缩放)
SetWindowLong(GetForegroundWindow(), GWL_STYLE, WS_POPUP); //设置拖拽缩放模式(未完全去掉(参数控制,即GetWindowLong(GetForegroundWindow(), GWL_STYLE)& ~WS_CAPTION | WS_THICKFRAME))
                     SetWindowLong(GetForegroundWindow(), GWL_STYLE, GetWindowLong(GetForegroundWindow(), GWL_STYLE)
& ~WS_CAPTION | WS_THICKFRAME);

//设置窗口位置及分辨率
bool result = SetWindowPos(GetForegroundWindow(), 0, (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height, SWP_SHOWWINDOW);


注:

1.windows下的窗口一旦去了上边栏(边框),就无法拖拽缩放了,边框和上边栏是一体的,所以拖拽功能要想保留不能直接去掉边框,当然可以自己写拖拽缩放,我试着写过,实现可以,但是无奈领导说最好使用原生的,我就放弃了。

2.还有一个坑,注意windows环境下,屏幕坐标原点(0,0)在左上角,不和unity中一样在左下角。

去边框上边框(自带白色上边框)效果:

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

<3>.最大化最小化窗口(没有上边框下,有的话就不用考虑这功能了)

导入user32.dll的相关方法以及参数:

  //最大最小化
private const int SW_SHOWMINIMIZED = ;//(最小化窗口)
private const int SW_SHOWMAXIMIZED = ;//最大化窗口

//设置当前窗口的显示状态
           [DllImport("user32.dll")]
           public static extern bool ShowWindow(System.IntPtr hwnd, int nCmdShow);

使用:

  /// <summary>
/// 最小化窗口
/// </summary>
public void SetMinWindows()
{
ShowWindow(GetForegroundWindow(), SW_SHOWMINIMIZED);
} /// <summary>
/// 最大化窗口
/// </summary>
public void SetMaxWindows()
{
ShowWindow(GetForegroundWindow(), SW_SHOWMAXIMIZED);
}

注:这里的最大化窗口是指全屏铺满,如果想要那种普通exe的最大化(保留任务栏),需要自己去掉任务栏高再设置分辨率以及定位置

<4>.拖动窗口

导入user32.dll的相关方法:

 //窗口拖动
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
[DllImport("user32.dll")]
public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam); //获取当前激活窗口
[DllImport("user32.dll", EntryPoint = "GetForegroundWindow")]
public static extern System.IntPtr GetForegroundWindow();

使用:

        /// <summary>
/// 拖动窗口
/// </summary>
/// <param name="window">当前句柄</param>
public void DragWindow(IntPtr window)
{
ReleaseCapture();
SendMessage(window, 0xA1, 0x02, );
SendMessage(window, 0x0202, , );
}

注:里面参数都是默认的,无需改(重点是很多参数,想搞明白去查下user32 的API)。

<5>.更改标题栏

导入user32.dll的相关方法:

         //更改标题栏
[DllImport("user32.dll")]
public static extern int SetWindowText(IntPtr hWnd, string text);
         /// <summary>
/// 改变标题栏标题
/// </summary>
public void ChangeTitleText()
{
SetWindowText(GetForegroundWindow(), string.Empty);
}

<6>.查找任务栏,并获取任务栏高度(这个需求是因为该库中最大化是指全屏铺满,但是我们需要的最大化时保留下方任务栏)

导入user32.dll的相关方法:

         //使用查找任务栏
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string strClassName, int nptWindowName);
         /// <summary>
/// 获取任务栏高度
/// </summary>
/// <returns>任务栏高度</returns>
private int GetTaskBarHeight()
{
int taskbarHeight = ;
IntPtr hWnd = FindWindow("Shell_TrayWnd", );
RECT rect = new RECT();
GetWindowRect(hWnd, ref rect);
taskbarHeight = rect.Bottom - rect.Top;
return taskbarHeight;
}

此时设置想要保留任务栏的最大化:

         /// <summary>
/// 最大化窗口
/// </summary>
public void SetMaxWindows()
{
int currMaxScreenHeight = Screen.currentResolution.height - GetTaskBarHeight();
SetWindowPos(GetForegroundWindow(), , , , Screen.currentResolution.width, currMaxScreenHeight, SWP_SHOWWINDOW);
// ShowWindow(GetForegroundWindow(), SW_SHOWMAXIMIZED);
}

注:Screen.currentResolution.height 当前设备高,Screen.currentResolution.width 当前设备宽

保留任务栏最大化效果:

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

<7>.获取当前窗口句柄的分辨率

导入user32.dll的相关方法:

         //获取窗口位置以及大小
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; //最左坐标
public int Top; //最上坐标
public int Right; //最右坐标
public int Bottom; //最下坐标
}

使用:

         /// <summary>
/// 获取当前窗口尺寸
/// </summary>
/// <returns></returns>
public Rect GetWindowInfo()
{
RECT rect = new RECT();
Rect targetRect = new Rect();
GetWindowRect(GetForegroundWindow(), ref rect);
targetRect.width = Mathf.Abs(rect.Right - rect.Left);
targetRect.height = Mathf.Abs(rect.Top - rect.Bottom); //锚点在左上角
targetRect.x = rect.Left;
targetRect.y = rect.Top;
return targetRect;
}
注:锚点在左上角 targetRect.x = rect.Left; targetRect.y = rect.Top;所以拿的是左上。

完整代码:

   /// <summary>
/// 窗口工具系统类(窗口状态)
/// </summary>
public class WindowsTool
{ #region 系统字段 & 系统方法 //设置当前窗口的显示状态
[DllImport("user32.dll")]
public static extern bool ShowWindow(System.IntPtr hwnd, int nCmdShow); //获取当前激活窗口
[DllImport("user32.dll", EntryPoint = "GetForegroundWindow")]
public static extern System.IntPtr GetForegroundWindow(); //设置窗口边框
[DllImport("user32.dll")]
public static extern IntPtr SetWindowLong(IntPtr hwnd, int _nIndex, int dwNewLong); //设置窗口位置,尺寸
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex); //窗口拖动
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
[DllImport("user32.dll")]
public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam); //更改标题栏
[DllImport("user32.dll")]
public static extern int SetWindowText(IntPtr hWnd, string text); //使用查找任务栏
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string strClassName, int nptWindowName); //获取窗口位置以及大小
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; //最左坐标
public int Top; //最上坐标
public int Right; //最右坐标
public int Bottom; //最下坐标
} //边框参数
private const uint SWP_SHOWWINDOW = 0x0040;
private const int GWL_STYLE = -;
private const int WS_BORDER = ; //隐藏标题栏图标
private const int WS_POPUP = 0x800000;
private const int WS_SYSMENU = 0x80000; //最大最小化
private const int SW_SHOWMINIMIZED = ;//(最小化窗口)
private const int SW_SHOWMAXIMIZED = ;//最大化窗口 //去除标题栏保留边框
private const int WS_CAPTION = 0x00C00000;
private const int WS_THICKFRAME = 0x00040000; #endregion #region 方法 /// <summary>
/// 最小化窗口
/// </summary>
public void SetMinWindows()
{
ShowWindow(GetForegroundWindow(), SW_SHOWMINIMIZED);
} /// <summary>
/// 最大化窗口
/// </summary>
public void SetMaxWindows()
{
int currMaxScreenHeight = Screen.currentResolution.height - GetTaskBarHeight();
SetWindowPos(GetForegroundWindow(), , , , Screen.currentResolution.width, currMaxScreenHeight, SWP_SHOWWINDOW);
// ShowWindow(GetForegroundWindow(), SW_SHOWMAXIMIZED);
} /// <summary>
/// 设置无边框,窗口位置及分辨率
/// </summary>
/// <param name="rect">尺寸数据</param>
public void SetNoFrameWindow(Rect rect, bool isMax, bool isDrag = false)
{
if (!isDrag)
{
//去除上边栏(不可拖拽缩放)
SetWindowLong(GetForegroundWindow(), GWL_STYLE, WS_POPUP);
}
else
{
if (!isMax)
{
//设置拖拽缩放模式
SetWindowLong(GetForegroundWindow(), GWL_STYLE, GetWindowLong(GetForegroundWindow(), GWL_STYLE)
& ~WS_CAPTION | WS_THICKFRAME);
}
else {
//去除上边栏(不可拖拽缩放)
SetWindowLong(GetForegroundWindow(), GWL_STYLE, WS_POPUP);
}
} //隐藏上边栏(部分)
// SetWindowLong(GetForegroundWindow(), GWL_STYLE, GetWindowLong(GetForegroundWindow(), GWL_STYLE) & ~WS_POPUP); if (isMax)
{
SetMaxWindows();
}
else
{
//设置窗口位置及分辨率
bool result = SetWindowPos(GetForegroundWindow(), , (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height, SWP_SHOWWINDOW);
}
} /// <summary>
/// 拖动窗口
/// </summary>
/// <param name="window">当前句柄</param>
public void DragWindow(IntPtr window)
{
ReleaseCapture();
SendMessage(window, 0xA1, 0x02, );
SendMessage(window, 0x0202, , );
} /// <summary>
/// 改变标题栏标题
/// </summary>
public void ChangeTitleText()
{
SetWindowText(GetForegroundWindow(), string.Empty);
} /// <summary>
/// 获取当前窗口尺寸
/// </summary>
/// <returns></returns>
public Rect GetWindowInfo()
{
RECT rect = new RECT();
Rect targetRect = new Rect();
GetWindowRect(GetForegroundWindow(), ref rect);
targetRect.width = Mathf.Abs(rect.Right - rect.Left);
targetRect.height = Mathf.Abs(rect.Top - rect.Bottom);
targetRect.x = rect.Left;
targetRect.y = rect.Top;
return targetRect;
} #endregion #region Private Methods /// <summary>
/// 获取任务栏高度
/// </summary>
/// <returns>任务栏高度</returns>
private int GetTaskBarHeight()
{
int taskbarHeight = ;
IntPtr hWnd = FindWindow("Shell_TrayWnd", );
RECT rect = new RECT();
GetWindowRect(hWnd, ref rect);
taskbarHeight = rect.Bottom - rect.Top;
return taskbarHeight;
} #endregion
}

最新:发布后windows扩展模式下窗体显示错乱不对的情况?

几种扩展特殊扩展屏幕方式,比如:

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

这四种方式,大家都知道,扩展屏就是在原有基本尺寸屏幕上,扩了一定尺寸的屏幕显示,实质上还是一个屏(计算机内部认为)

初步分析下:

1.左右扩展:往左扩展就在X方向减去初始屏幕的分辨率宽,往右扩展就在X方向加上初始屏幕的分辨率宽。

2.上下扩展:往上扩展就在Y方向加上初始屏幕的分辨率高,往下扩展就在Y方向减去初始屏幕的分辨率高。

姑且这样认为。测试一下,这个地方的坐标是不是我们认为的这种(左-,右+,上+,下-),这个地方我测了下,如图:

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

1区(X>0 & Y<0),2区(X<0 & Y<0),3区(X<0 & Y>0),5区(X>0 & Y>0),4区是窗口区域,左上角是窗口锚点o(0,0)点。

原则搞清楚了不是我们想的那样。按我们上面分析的,到这里你可以解决上述四种扩展方式屏幕的问题,但是,恶心的事发生了(一直没注意),扩展屏幕的排列方式很*,比如:

图1.

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

图2.

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

图3.

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

图4.

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

瞬间让我感觉windows太JB没底线了,给老子加难度。

下面再仔细分析一下还有什么问题?

我想知道这俩排列的高度差,也就是分屏的信息,主机连接了几个显示器?各自分辨率是多少?屏幕锚点坐标是什么?我们需要这些信息!不然根本没法确定窗口位置。

Unity中获取主机连接了几个显示器,显示器的分辨率以及位置这些是没提供给我们相应的API的。

有人说有Resolution[] resolutions = Screen.resolutions;

这不是所有显示器分辨率列表

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

这是说明,自己看看,也可以试试,绝不是你想象中的那样亦或者你仅仅可以获取当前屏幕的分辨率。

还有人说Display这是unity中的分屏处理,具体自己去查查吧,比较官方。

所以这里我使用了WinForm中的API,即主要是System.Windows.Forms.dll这个库中的Screen.AllScreens函数获取显示器信息,显示器数量,位置,分辨率等信息。

间接引用到System.Drawing.dll但是在引用这个库时遇到诸多问题:

1.System.Windows.Forms.dll正常引用方式引用不进来(在vs“引用”中直接应用,原因不详,可能是与问题2所说的有关系),只能将System.Windows.Forms.dll放到工程中来,在Plugins下,当作其他那种dll用

2.由于.NET类库System.Drawing.dll提供了一系列的图形函数,但由于其使用的是GDI接口,与DirectX和OpenGL之间不兼容,在Unity中默认是不被支持的。引用方式如1.

3.引用进来后打包出错,或者导进去直接报错(具体忘记是什么了),找到playerSetting中的Other Settings中的Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug改成如图。(还有错误就重启Unity,可能需要重新配置)

我记忆中会有以上的三个问题,最后我的解决方案是:

1.无需将这两个DLL放入工程

2.写一名为csc.rsp的文件,内容是-r:System.Windows.Forms.dll -r:System.Drawing.dll。-r开头,想引用多个dll就空格后继续-r,然后将这文件放到Assets文件夹下(根目录)然后就搞定了,引用成功。

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

具体代码:

获取第一显示器的锚点和分辨率

//Y坐标       
int Y = System.Windows.Forms.Screen.AllScreens[].Bounds.Y;
//X坐标
int X = System.Windows.Forms.Screen.AllScreens[].Bounds.X;
//高度
int height = System.Windows.Forms.Screen.AllScreens[].Bounds.Height;
//宽度
int width = System.Windows.Forms.Screen.AllScreens[].Bounds.Width;

最大化窗口(不同显示器定位搞定):

App.initDeviceWidth 初始显示器宽,
App.initDeviceHeight 初始显示器高

(里面没说明的函数,上面去找)
         /// <summary>
/// 最大化窗口
/// </summary>
public void SetMaxWindows()
{
int currMaxScreenHeight = Screen.currentResolution.height - GetTaskBarHeight();
Rect rect = GetWindowInfo();
Y = System.Windows.Forms.Screen.AllScreens[].Bounds.Y;
X = System.Windows.Forms.Screen.AllScreens[].Bounds.X;
height = System.Windows.Forms.Screen.AllScreens[].Bounds.Height;
width = System.Windows.Forms.Screen.AllScreens[].Bounds.Width; //右
if (rect.x >= App.initDeviceWidth)
{
if (Y < )//上
{
SetWindowPos(GetForegroundWindow(), , (int)(width - App.initDeviceWidth), Y, Screen.currentResolution.width, currMaxScreenHeight, SWP_SHOWWINDOW);
}
else
{
SetWindowPos(GetForegroundWindow(), , (int)App.initDeviceWidth, (int)(height - App.initDeviceHeight), Screen.currentResolution.width, currMaxScreenHeight, SWP_SHOWWINDOW);
}
return;
}
else if (rect.x < )//左
{
//上
if (Y < )
{
SetWindowPos(GetForegroundWindow(), , X, Y, Screen.currentResolution.width, currMaxScreenHeight, SWP_SHOWWINDOW);
}
else
{
SetWindowPos(GetForegroundWindow(), , -(int)App.initDeviceWidth, (int)(height - App.initDeviceHeight), Screen.currentResolution.width, currMaxScreenHeight, SWP_SHOWWINDOW);
}
return;
}
else if (rect.y >= App.initDeviceHeight || rect.y < )
{
//正下
if (Y == )
{
SetWindowPos(GetForegroundWindow(), , , (int)App.initDeviceHeight, Screen.currentResolution.width, currMaxScreenHeight, SWP_SHOWWINDOW);
}
else
{
SetWindowPos(GetForegroundWindow(), , , -(int)App.initDeviceHeight, Screen.currentResolution.width, currMaxScreenHeight, SWP_SHOWWINDOW);
}
return;
}
else
{
SetWindowPos(GetForegroundWindow(), , , , Screen.currentResolution.width, currMaxScreenHeight, SWP_SHOWWINDOW);
}
}

显示器信息拿到了,设置位置就全是算数问题了,

但是有如下几坑:

1.如上图1234,扩展屏这个地方拿到的是Hright和width其实是下图的黑框大小(默认为组合屏的(两个屏幕的最外围矩形)尺寸),位置xy指的红圈处的坐标

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

2.图1这种情况左上角锚点(x=0 &y<0),图2这种情况左上角锚点(x=0 &y=0),图3这种情况左上角锚点(x<0 &y<0),图4这种情况左上角锚点(x<0 &y=0)

图二和图四是坑,容易想错,注意!!!

完美解决!

最新问题:上面一系列操作最近遇到了一点问题,就是使用获取窗口的方式用的有错误,我使用的是获取当前激活窗口GetForegroundWindow();在你切换exe时,可能操作的窗口就不是你原本想操作的那个窗口了,我们应该使用一个方法,获取指定窗口,无论你切换到哪个窗口都不能影响,这个方法是FindWindow();

具体方法:

        //获取指定unity.exe窗口
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public static IntPtr ParenthWnd = FindWindow(null, "ProjectName"); //build时候的项目名,playerSetting中设置的

用这个方法替换掉GetForegroundWindow()就可以解决了,这样就可以避免由于切换窗口失去指定窗口句柄的问题。

最新问题:Unity中调用windows窗口对文件进行筛选功能,文件类型自定义。如:

Unity中调用Windows窗口句柄以及根据需求设置并且解决扩展屏窗体显示错乱/位置错误的Bug

主要是:设置filter,并且结合pth.filterIndex去选择使用生成的类型列表中的第几个(注意是从1开始的)。

 //\0字符串分隔符(多个根据\0分割的字符串形成下拉列表);
//pth.filter = "JSON file(*.json)\0*.json\0FBX file(*.fbx)\0*.fbx\0"; //* \0*.fbx";
//;是&的作用 1:.json;.fbx;.gltf 2:.json 3:.fbx 4:.gltf 5:PNG 6:All Files
pth.filter = "模型文件(*.json|*.fbx|*.gltf)\0*.json;*.fbx;*.gltf\0JSON file(*.json)\0*.json\0FBX file(*.fbx)\0*.fbx\0GLTF file(*.gltf)\0*.gltf\0PNG file(*.png)\0*.png\0All Files\0*.*\0\0";

完整代码:

  /// <summary>
/// 文件过滤器类型
/// </summary>
public enum FileFilterType
{ /// <summary>
/// 模型文件(.json/.gltf/.fbx) 1
/// </summary>
Model_File, /// <summary>
/// json文件(.json) 2
/// </summary>
JSON_File, /// <summary>
/// FBX文件(.fbx) 3
/// </summary>
FBX_File, /// <summary>
/// GLTF文件(.gltf) 4
/// </summary>
GLTF_File, /// <summary>
/// PNG文件(.png) 5
/// </summary>
PNG_File, /// <summary>
/// ALLFile 6
/// </summary>
AllFile,
} /// <summary>
/// 打开项目弹框
/// </summary>
/// <returns></returns>
private void OpenWindow(string foldName, FileFilterType fileFilterType, string defExtString, Action<string> action)
{
string filepath = "";
OpenFile pth = new OpenFile();
pth.structSize = System.Runtime.InteropServices.Marshal.SizeOf(pth); //\0字符串分隔符(多个根据\0分割的字符串形成下拉列表);
//pth.filter = "JSON file(*.json)\0*.json\0FBX file(*.fbx)\0*.fbx\0"; //* \0*.fbx";
//;是&的作用 1:.json;.fbx;.gltf 2:.json 3:.fbx 4:.gltf 5:PNG 6:All Files
pth.filter = "模型文件(*.json|*.fbx|*.gltf)\0*.json;*.fbx;*.gltf\0JSON file(*.json)\0*.json\0FBX file(*.fbx)\0*.fbx\0GLTF file(*.gltf)\0*.gltf\0PNG file(*.png)\0*.png\0All Files\0*.*\0\0";
pth.filterIndex = ((int)fileFilterType + );
pth.file = new string(new char[]);
pth.maxFile = pth.file.Length;
pth.fileTitle = new string(new char[]);
pth.maxFileTitle = pth.fileTitle.Length;
pth.initialDir = Application.dataPath; // default path
pth.title = foldName;
pth.defExt = defExtString; //注意 一下项目不一定要全选 但是0x00000008项不要缺少
//0x00080000 是否使用新版文件选择窗口,0x00000200 是否可以多选文件
pth.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000200 | 0x00000008; if (OpenFileWindow.GetOpenFileName(pth))
{
filepath = pth.file;//选择的文件路径;
action(filepath);
}
}

欢迎交流指正。

上一篇:ubuntu安装vim时提示 没有可用的软件包 vim,但是它被其它的软件包引用了 解决办法


下一篇:网络统计学与web前端开发基础技术