sexta-feira, 27 de julho de 2018

Como definir a resolução do monitor com o C#

Tive dificuldade em definir uma resolução de tela dinâmica quando estava fazendo um aplicativo. Eu presumo que a maioria de nós tenha acontecido conosco ou tenha enfrentado esses tempos em algum lugar ao longo de nosso ciclo de vida de engenharia. Porque, como todos sabemos, a resolução da tela do usuário pode não ser a mesma da tela do ambiente de desenvolvimento.

Neste post vou entregar o que eu encontrei como uma solução para o desafio discutido.
Então vamos ver:


  • Como obter a resolução do monitor do usuário.
  • Como alterar a resolução do monitor do usuário para produto compatível
  • Como proteger a resolução do monitor do usuário.

Como obter a resolução do monitor do usuário


O acesso à tela do usuário está sendo facilitado pela classe Screen, que é fornecida junto com a estrutura .NET. E pode acessar a resolução da tela do usuário atual por meio de uma propriedade Screen.PrimaryScreen que é estática disponível na classe Screen:

public static Screen PrimaryScreen {get;}

A propriedade acima mencionada é somente leitura e retorna um tipo  Screen. E a lógica abaixo mostra como usar o tipo Screen para acessar a resolução da tela do usuário:

Screen screen = Screen.PrimaryScreen;
int S_width=screen.Bounds.Width;

int S_height=screen.Bounds.Height;



Como alterar a resolução do monitor do usuário para produto compatível


Antes de seguir em direção ao nosso próximo objetivo, deixe-me falar sobre a parte não gerenciada dessa implementação. Ao contrário das linguagens tradicionais, a estrutura .NET mantém uma etapa distinta enquanto aproveita o código gerenciado e não gerenciado. Pessoalmente, quando escrevi este artigo, nunca encontrei nenhum código gerenciado que faça esse tratamento de resolução. E isso é o que me fez pensar em explorar algumas APIs do Win32.

Como o escopo deste artigo está limitado ao código gerenciado, não discutirei nada sobre código não gerenciado. Mas, apesar disso, podemos usar o atributo DllImport para ler a definição de código não gerenciado em seu ambiente gerenciado. Neste caso, estaremos usando a API User32.dll, que facilita a resolução dinâmica e possui duas funções relacionadas à alteração da resolução da tela.


  • EnumDisplaySettings
  • ChangeDisplaySettings


class User32
{
        [DllImport("user32.dll")]
        public static extern int EnumDisplaySettings (string deviceName, int modeNum, ref DEVMODE devMode );         
        [DllImport("user32.dll")]
        public static extern int ChangeDisplaySettings(
              ref DEVMODE devMode, int flags); 

        public const int ENUM_CURRENT_SETTINGS = -1;
        public const int CDS_UPDATEREGISTRY = 0x01;
        public const int CDS_TEST = 0x02;
        public const int DISP_CHANGE_SUCCESSFUL = 0;
        public const int DISP_CHANGE_RESTART = 1;
        public const int DISP_CHANGE_FAILED = -1;
}


Como sabemos, o [DllImport("user32.dll")] é um trabalho explícito antes de injetar uma implementação não gerenciada em nosso ambiente gerenciado.


public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode);

DEVMODE é uma estrutura explicada na documentação da plataforma, bem como incluída no Visual Studio .NET. A estrutura é definida em C#, por isso não se preocupar!


[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE 
{
        [MarshalAs(UnmanagedType.ByValTStr,SizeConst=32)] 
          public string dmDeviceName;
        public short  dmSpecVersion;
        public short  dmDriverVersion;
        ... 
}

Você pode dar uma olhada no código-fonte clicando aqui e aplicar essa lógica a qualquer evento desejado.


Esteja ciente de que, os tipos têm o tamanho certo e que as seqüências de comprimento fixo são apropriadamente definida. Neste caso, WORD mapeia para short, DWORD para int e short permanece como short.

DEVMODE dm = new DEVMODE();
dm.dmDeviceName = new String(new char[32]);
dm.dmFormName = new String(new char[32]);
dm.dmSize = (short)Marshal.SizeOf (dm); 

if (0 !=User32.EnumDisplaySettings(null,User32.ENUM_CURRENT_SETTINGS,ref dm))
{

Neste ponto, a estrutura DEVMODE será decorado com as configurações padrão e pode modificá-la em qualquer instância.

dm.dmPelsWidth = iWidth;
dm.dmPelsHeight = iHeight; 
int iRet = User32.ChangeDisplaySettings(ref dm, User32.CDS_UPDATEREGISTRY);


O bloco de código fechado faz isso um pouco diferente de lidar com várias condições de erro. Gostaria de incentivá-lo a olhar para o arquivo fonte completo e ver o que ele faz. Isso é tudo que existe para isso.


Como proteger a resolução de tela do usuário

Finalmente, antes de continuarmos, é nossa responsabilidade salvaguardar a resolução de tela padrão de um usuário. Para fazer isso, você pode ter que usar alguns membros ou classes estáticas para manter a resolução da tela do usuário e retê-la quando concluir a execução.


Bonus - O código-fonte completo


Fim de conversa, para ver o código-fonte de graça clica aqui.
Espero ter ajudado. Força!

Resolução da tela com C#


Cria uma classe e cole o código abaixo. Se não sabe como usar este código veja o meu post anterior clicando aqui.


using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE1
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmDeviceName;
    public short dmSpecVersion;
    public short dmDriverVersion;
    public short dmSize;
    public short dmDriverExtra;
    public int dmFields;

    public short dmOrientation;
    public short dmPaperSize;
    public short dmPaperLength;
    public short dmPaperWidth;

    public short dmScale;
    public short dmCopies;
    public short dmDefaultSource;
    public short dmPrintQuality;
    public short dmColor;
    public short dmDuplex;
    public short dmYResolution;
    public short dmTTOption;
    public short dmCollate;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmFormName;
    public short dmLogPixels;
    public short dmBitsPerPel;
    public int dmPelsWidth;
    public int dmPelsHeight;

    public int dmDisplayFlags;
    public int dmDisplayFrequency;

    public int dmICMMethod;
    public int dmICMIntent;
    public int dmMediaType;
    public int dmDitherType;
    public int dmReserved1;
    public int dmReserved2;

    public int dmPanningWidth;
    public int dmPanningHeight;
};



class User_32
{
    [DllImport("user32.dll")]
    public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE1 devMode);
    [DllImport("user32.dll")]
    public static extern int ChangeDisplaySettings(ref DEVMODE1 devMode, int flags);

    public const int ENUM_CURRENT_SETTINGS = -1;
    public const int CDS_UPDATEREGISTRY = 0x01;
    public const int CDS_TEST = 0x02;
    public const int DISP_CHANGE_SUCCESSFUL = 0;
    public const int DISP_CHANGE_RESTART = 1;
    public const int DISP_CHANGE_FAILED = -1;
}


namespace Resolution
{
    class CResolution
    {
        public CResolution(int a, int b)
        {
            Screen screen = Screen.PrimaryScreen;


            int iWidth = a;
            int iHeight = b;


            DEVMODE1 dm = new DEVMODE1();
            dm.dmDeviceName = new String(new char[32]);
            dm.dmFormName = new String(new char[32]);
            dm.dmSize = (short)Marshal.SizeOf(dm);

            if (0 != User_32.EnumDisplaySettings(null, User_32.ENUM_CURRENT_SETTINGS, ref dm))
            {

                dm.dmPelsWidth = iWidth;
                dm.dmPelsHeight = iHeight;

                int iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_TEST);

                if (iRet == User_32.DISP_CHANGE_FAILED)
                {
                    MessageBox.Show("Não é possível processar seu pedido");
                    MessageBox.Show("Descrição: Não é possível processar seu pedido. Desculpe por esta Inconveniência. ", "Informação", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_UPDATEREGISTRY);

                    switch (iRet)
                    {
                        case User_32.DISP_CHANGE_SUCCESSFUL:
                            {
                                break;
                                // mudança bem sucedida
                            }
                        case User_32.DISP_CHANGE_RESTART:
                            {
                                MessageBox.Show("Descrição: Você precisa reiniciar para que a mudança aconteça. \n Se você sentir qualquer problema após a reinicialização da sua máquina \nEntão tente alterar a resolução no modo de segurança. ", "Informação", MessageBoxButtons.OK, MessageBoxIcon.Information);
                                break;
                                // windows 9x series você tem que reiniciar
                            }
                        default:
                            {
                                MessageBox.Show("Descrição: Falha ao alterar a resolução.", "Informação", MessageBoxButtons.OK, MessageBoxIcon.Information);
                                break;
                                // não conseguiu alterar
                            }
                    }
                }

            }
        }
    }
}


Pesquisar neste blogue

Com tecnologia do Blogger.

Páginas