Tuesday, July 14, 2009

Sử dụng SystemParametersInfo để thay đổi thiết lập giao diện

Hàm SystemParametersInfo cho phép bạn truy cập và thay đổi hầu hết các thiết lập giao diện người sử dụng ở mức thấp (màn hình Display Properties của Windows).

Bạn có thể đọc chi tiết trên MSDN, ở đây tôi chỉ đưa một số ví dụ cơ bản:

  • SPI_GETICONTITLELOGFONT giúp bạn lấy thông tin font chữ được sử dụng cho các nhãn icon; SPI_SETICONTITLELOGFONT giúp bạn thay đổi thông tin này.
  • SPI_GETNONCLIENTMETRICS giúp bạn lấy thông tin về font chữ được sử dụng trong các màn hình của Window bao gồm captions, menus, status bars, và message boxes; SPI_SETNONCLIENTMETRICS giúp bạn thay đổi chúng.

Và một số thiết lập trong Control Panel như:

  • SPI_SETKEYBOARDDELAY và SPI_SETKEYBOARDSPEED giúp bạn thiết lập các tham số của keyboard.
  • SPI_SETDOUBLECLICKTIME giúp bạn thiết lập tốc độ double-click của con chuột.
  • SPI_SETMENUFADE giúp bạn thiết lập hoặc vô hiệu hóa menu fade animation.

Sau đây, tôi sẽ viết một đoạn chương trình demo để thay đổi thiết lập font chữ của MessageBox trên Windows.

    public class WindowsUISettings
    {
        // Khai báo sử dụng API SystemParametersInfo
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern bool SystemParametersInfo(
            int Action, int uiParam, [In, Out] NONCLIENTMETRICS pvParam, int WinIni);

        [StructLayout(LayoutKind.Sequential)]
        private class NONCLIENTMETRICS
        {
            public int cbSize = Marshal.SizeOf(typeof(NONCLIENTMETRICS));
            public int iBorderWidth;
            public int iScrollWidth;
            public int iScrollHeight;
            public int iCaptionWidth;
            public int iCaptionHeight;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfCaptionFont;
            public int iSmCaptionWidth;
            public int iSmCaptionHeight;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfSmCaptionFont;
            public int iMenuWidth;
            public int iMenuHeight;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfMenuFont;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfStatusFont;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfMessageFont;
        }


        private const int LF_FACESIZE = 32;

        // A "logical font" used by old-school windows
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private class LOGFONT
        {
            public int lfHeight;
            public int lfWidth;
            public int lfEscapement;
            public int lfOrientation;
            public int lfWeight;
            public byte lfItalic;
            public byte lfUnderline;
            public byte lfStrikeOut;
            public byte lfCharSet;
            public byte lfOutPrecision;
            public byte lfClipPrecision;
            public byte lfQuality;
            public byte lfPitchAndFamily;
            
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]
            public string lfFaceName;

            // to shut it up about the warnings
            public LOGFONT(string lfFaceName)
            {
                this.lfFaceName = lfFaceName;
                lfHeight = lfWidth = lfEscapement = lfOrientation = lfWeight = 0;
                lfItalic = lfUnderline = lfStrikeOut = lfCharSet = lfOutPrecision
                = lfClipPrecision = lfQuality = lfPitchAndFamily = 0;
            }
        }

        //Khai báo SPI lấy và thay đổi thiết lập về 
        //caption, menu, status bar, scroll bar và message box 
        private const int SPI_GETNONCLIENTMETRICS = 0x0029;
        private const int SPI_SETNONCLIENTMETRICS = 0x002A;

        private NONCLIENTMETRICS currentMetrics;

        /// <summary>
        /// Lưu giữ lại các giá trị ngầm định
        /// </summary>
        private static NONCLIENTMETRICS defaultMetrics;

        private WindowsUISettings()
        { }

        public static WindowsUISettings CurrentMetrics()
        {
            //Lưu giữ lại các giá trị ngầm định
            defaultMetrics = new NONCLIENTMETRICS();
            SystemParametersInfo(SPI_GETNONCLIENTMETRICS, defaultMetrics.cbSize, defaultMetrics, 0);

            NONCLIENTMETRICS pvParam = new NONCLIENTMETRICS();
            SystemParametersInfo(SPI_GETNONCLIENTMETRICS, pvParam.cbSize, pvParam, 0);

            WindowsUISettings instance = new WindowsUISettings();
            instance.currentMetrics = pvParam;

            return instance;
        }

        /// <summary>
        /// Lấy/Thiết lập font chữ cho MessageBox
        /// </summary>
        public Font MessageFont
        {
            get
            {
                return Font.FromLogFont(currentMetrics.lfMessageFont);
            }
            set
            {
                value.ToLogFont(currentMetrics.lfMessageFont);
            }
        }

        /// <summary>
        /// Thiết lập các thay đổi
        /// </summary>
        public void Apply()
        {
            SystemParametersInfo(SPI_SETNONCLIENTMETRICS, currentMetrics.cbSize, currentMetrics, 1);
        }

        /// <summary>
        /// Đặt lại các giá trị ngầm định
        /// </summary>
        public void RestoreDefault()
        {
            SystemParametersInfo(SPI_SETNONCLIENTMETRICS, defaultMetrics.cbSize, defaultMetrics, 1);
        }
    }

Và đây là ví dụ chạy thử:

Hãy tạo 1 form mới, trên đó có 2 button bao gồm, button1 và button2. Double-click vào button1 và sau đó button2, ví dụ xử lý sự kiện Click trên 2 button sẽ như sau:

    public partial class Form1 : Form
    {
        WindowsUISettings metrics 
            = WindowsUISettings.CurrentMetrics();

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Font messageFont = new Font(
                metrics.MessageFont.FontFamily, 14);

            metrics.MessageFont = messageFont;
            metrics.Apply();

            MessageBox.Show(
            "MessageBox đã được thay đổi font size thành 14.");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            metrics.RestoreDefault();
            MessageBox.Show(
            "MessageBox đã được trở về font size 8.");

        }
    }
Qua ví dụ này, chắc các bạn đã hiểu cách để lấy thông tin và thay đổi các thông số khác.

Monday, July 6, 2009

Cách chạy Crystal Report trên Windows Vista/7

Giới thiệu

Khi tôi chạy chương trình để hiển thị các báo cáo sử dụng Crystal Report XI trên Windows Vista hoặc Windows 7 thì luôn nhận được thông báo lỗi:

Description:
  Stopped working

Problem signature:
  Problem Event Name:    APPCRASH
  Application Name:    CRXISample.exe
  Application Version:    1.0.0.0
  Application Timestamp:    4a517191
  Fault Module Name:    unknown
  Fault Module Version:    0.0.0.0
  Fault Module Timestamp:    00000000
  Exception Code:    c0000005
  Exception Offset:    04b978c8
  OS Version:    6.1.7100.2.0.0.256.1
  Locale ID:    1041

Mô tả lỗi

Lỗi này xuất hiện do tính năng DEP (Data Execution Prevention) trên Windows Vista/7 khi được kích hoạt (enabled) thì sẽ ngăn chặn (block) tất cả các chương trình  đang cố gắng chạy mà không nằm trong danh sách tin cậy (trusted list).

Khắc phục lỗi

Lỗi này có thể được khắc phục bằng cách vô hiệu hóa tính năng DEP, tuy nhiên khi tính năng này được khóa thì có thể dẫn đến một số nguy hại đối với máy tính của bạn. Do đó, ở đây tôi sẽ đưa ra cách vô hiệu hóa tính năng này và có thể kích hoạt tính năng này nếu cần thiết.

Hình sau minh họa khi kích hoạt DEP và khi vô hiệu hóa DEP, ngầm định là DEP được kích hoạt.

DEP_Enabled DEP_Disabled

Kích hoạt DEP (Enabled)

1. Mở màn hình Command Prompt: Nhấn tổ hợp phím Windows+R; nhấn cmd và Enter.

2. Đánh dòng lệnh bcdedit.exe /set {current} nx OptIn và nhấn Enter.

3. Bạn có thể nhận được thông điệp báo thành công.

4. Khởi động lại máy.

Vô hiệu hóa DEP (Disabled)

1. Mở màn hình Command Prompt: Nhấn tổ hợp phím Windows+R; nhấn cmd và Enter.

2. Đánh dòng lệnh bcdedit.exe /set {current} nx AlwaysOff và nhấn Enter.

3. Bạn có thể nhận được thông điệp báo thành công.

4. Khởi động lại máy.

Kết luận

Cách vô hiệu hóa DEP có thể giúp nhiều chương trình chạy tốt trên môi trường Windows Vista hoặc Windows 7, tuy nhiên nó cũng có thể đem đến rắc rối với virus. Do đó, bạn nên cẩn thận khi sử dụng cách này.

Về Crystal Report bạn sẽ gặp vấn đề này trên hầu hết các phiên bản khi chạy trên Windows Vista hoặc Windows 7, tuy nhiên hiện nay phiên bản Crystal Report 2008 V1 (SP1) đã được khắc phục lỗi này. Do đó, bạn nên dùng CR 2008 V1 nếu có thể.