using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Test_KeyChk
{
    // KeyChk Secure-Communication Block (SCB).
    //   This memory block is used to pass information to/from the KeyChk DLL.
    // -------------------------------------------------------------------------
    // Please see the manual for a detailed description of each field.
    // -------------------------------------------------------------------------

    [StructLayout(LayoutKind.Sequential, Pack=1, Size=512, CharSet=CharSet.Ansi)]
    unsafe public struct SecureCommBlock      // 512 Bytes Total.
    {
        public       UInt16 err_code;         // 000 Main Error Code.
        public       UInt16 err_status1;      // 002 Error Status 2.
        public       UInt16 err_status2;      // 004 Error Status 3.
        public       Byte   func_code;        // 006 KeyChk Function Code.
        public fixed Byte   scb_id[5];        // 007 SCB Identification:
                                              //     s/b: "Kc.6" (ASCIIZ).
        public fixed Byte   prod_id[9];       // 012 Product-ID (Unicode UTF-8).
        public       Byte   reserved1;        // 021 Reserved.
        public       UInt16 feature_flags;    // 022 Feature-Flags 1-16:
                                              //     Least Significant Bit = Flag 1.
                                              //     Most Significant Bit = Flag 16.
        public       UInt32 prod_serial;      // 024 Product Serial Number;
        public       UInt32 prod_pin;         // 028 Product ID Number (PIN).
        public       Int16  uses_limit;       // 032 Uses Limit:
                                              //     -1:       Key is Unlimited.
                                              //     0:        Key is Expired.
                                              //     1-32,767: Number of Uses Left.
        public       UInt16 time_limit;       // 034 Time Limit:
                                              //     0xFFFF:   Key is Unlimited.
                                              //     0:        Key is Expired or on
                                              //                 Last Day of operation.
                                              //     1-65,534: Number Days Left.
        public fixed Byte   ed_ascii[9];      // 036 Expire-Date (ED) (ASCIIZ).
                                              //     Always in MM/DD/YY format.
        public fixed Byte   user_data[16];    // 045 User-Data.
        public fixed Byte   reserved2[6];     // 061 Reserved.
        public fixed Byte   lasered_id[8];    // 067 Key Lasered-ID (LID).
        public       Byte   renew_used_count; // 075 Renew Used-Count.
        public fixed Byte   reserved3[4];     // 076 Reserved.
        public fixed Byte   reg_name[64];     // 080 Registration Name (UTF-16 Unicode).
        public fixed Byte   reg_company[64];  // 144 Registration Company (UTF-16 Unicode).
        public fixed Byte   reg_other[64];    // 208 Registration Other Info (UTF-16 Unicode).
        public fixed Byte   expire_date[32];  // 272 Expire-Date (ED) (UTF-16 Unicode).
                                              //     Uses Local Region Settings for format.
        public fixed Byte   expire_time[32];  // 304 Expire-Time (ET) on last Day (UTF-16 Unicode)
                                              //     Uses Local Region Settings for format.
        public fixed Byte   reserved4[176];   // 336 Reserved.
    }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        [DllImport("KeyChk.dll", CallingConvention = CallingConvention.StdCall)]
        extern static void KeyChk(ref SecureCommBlock scb);

        private void button1_Click(object sender, EventArgs e)
        {
            // Button 1 was just Clicked.
            //
            // Call the Do_KeyChk_Call() function specifying '1'.
            //
            // That will call KeyChk to:
            //   Verify the presence of the Key with this Product-ID and PIN.
            //
            Do_KeyChk_Call(1);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // Button 2 was just Clicked.
            //
            // Call the Do_KeyChk_Call() function specifying '2'.
            //
            // That will call KeyChk to:
            //   Verify the presence of the Key with this Product-ID and PIN
            //     AND Update the Uses Limit of the Key.
            //
            Do_KeyChk_Call(2);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        unsafe private void Do_KeyChk_Call(byte keychk_func)
        {
            // This function setups the SCB and calls the KeyChk DLL.
            //
            // The Caller has set the keychk_func parameter to the
            //     KeyChk Function to use.

            // Allocate a block of memory for the SCB.
            SecureCommBlock scb = new SecureCommBlock();

            // Start by checking that the size of the SCB Structure is correct.
            //   Test programs are sometimes used to experiment...
            int scb_size = Marshal.SizeOf(scb);
            if (scb_size != 512)
            {
                MessageBox.Show(
                    "Size of SCB Structure Must Be 512 Bytes.\n" +
                    "    It's size is currently " + scb_size.ToString() + " Bytes.",
                    "KeyChk DLL Test Error",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation,
                    MessageBoxDefaultButton.Button1);
                return;
            }

            // Before calling KeyChk, set the SCB Error Code to "SCB Not Found".
            //   If, for some reason, KeyChk can't find the SCB memory block,
            //   it will not be able to access and set the "err_code" SCB field.
            //   Pre-Setting this error code allows us to know if this happened.
            scb.err_code = 0x6240;

            // Before calling KeyChk, set the "func_code" SCB field
            //   to what the Caller passed to us in the "keychk_func" parameter.
            scb.func_code = keychk_func;

            // Before calling KeyChk, initialize the "scb_id" SCB field so
            //   KeyChk will know that it's using the correct memory block.
            byte[] sig = Encoding.ASCII.GetBytes("Kc.6");
            for (int i = 0; i < 4; i++)
            {
                scb.scb_id[i] = sig[i];
            }
            scb.scb_id[4] = 0;      // Add terminator byte.

            // Before calling KeyChk, set the "prod_id" SCB field to "Examples"
            //   since this is the Product-ID of the Examples Key.
            //   The "Product-ID" screen field has already been set to "Examples".
            // This field must match the Product-ID of the Key that you want to find.
            //   In your Source Code, you would set this field to the Product-ID
            //     of your Product. This is shown on the KeyBuild "Build Keys" panel.
            // The Product-ID is a zero-terminated, case-sensitive, UTF8 Unicode field.
            byte[] utf8bytes = Encoding.UTF8.GetBytes(textBox1.Text);
            int size = utf8bytes.Length;
            // Copy the UTF8 bytes into Product-ID SCB field.
            for (int i = 0; i < size; i++)
                scb.prod_id[i] = utf8bytes[i];
            scb.prod_id[size] = 0;      // Add terminator byte.

            // Before calling KeyChk, set the "prod_serial" field to Zero
            //   to skip the validation of the Key's Serial Number.
            // Normally, you will set the "prod_serial" SCB field to zero so
            //   the Key check will succeed as long as KeyChk finds a Key
            //   with your specific Product-ID and Product PIN.
            //   In this case, the Serial Number of the Key found
            //     will be returned in the "prod_serial" SCB field.
            // If you want to verify that the Key present has a
            //   specific Product Serial Number, then set this field to
            //   the specific Serial Number that you want to verify.
            scb.prod_serial = 0;

            // Before calling KeyChk, set the "Prod_PIN" SCB field to "375391873"
            //   since this is the Product PIN of the Examples Key.
            //   The "Product PIN" screen field has already been set to "375391873".
            // This field must match the Product PIN of the Key that you want to find.
            //   In your Source Code, you would set this field to the Product PIN
            //     of your Product. This is shown on the KeyBuild "Build Keys" panel.
            UInt64 pin = 0;
            try
            {
                pin = Convert.ToUInt64(textBox2.Text);
            }
            catch
            {
                pin = 0;
            }
            if ((pin < 1) || (pin > 0xFFFFFFFF))
            {
                MessageBox.Show("Product PIN Must Be From 1 to 4,294,967,295.",
                    "KeyChk DLL Test Error",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation,
                    MessageBoxDefaultButton.Button1);
                return;
            }
            scb.prod_pin = Convert.ToUInt32(textBox2.Text);

            // Before calling KeyChk, copy the "Uses Limit Update" screen field
            //   to the "Uses_Limit" SCB field.
            // NOTE: The Uses Limit of the Key is only updated
            //       if KeyChk Function 2 or 3 is used.
            // To update the Uses Limit of the Key from this Example program,
            //   set the "Uses Limit Update" screen field to '-1', '-2' or '-3'
            //   and then click the "Do KeyChk Function 2" button.
            scb.uses_limit = (sbyte)numericUpDown1.Value;

            // Before calling KeyChk,
            //   clear out all the screen field results from the last run.
            textBox3.Text = "";
            textBox4.Text = "";
            textBox5.Text = "";
            textBox6.Text = "";
            textBox7.Text = "";
            textBox8.Text = "";
            textBox9.Text = "";
            textBox10.Text = "";
            textBox11.Text = "";
            textBox12.Text = "";
            textBox13.Text = "";
            textBox14.Text = "";
            textBox15.Text = "";
            textBox16.Text = "";

            // Call the KeyChk DLL using the values setup in the SCB.
            //   This will check for the presence of the Key that matches
            //     the Product-ID and Product PIN fields.
            // If the Key is found, all the information from the Key will
            //   be returned in the SCB.
            KeyChk(ref scb);

            // Display the Result of the Key Check operation.
            if (scb.err_code == 0)
                textBox3.Text = "The Key was Found and is Not Expired.";
            else if (scb.err_code == 0x6043)
                textBox3.Text = "The Key was Not Found.";
            else if (scb.err_code == 0x6142)
                textBox3.Text = "The Key was Found but the Uses Limit is Expired.";
            else if (scb.err_code == 0x6143)
                textBox3.Text = "The Key was Found but the Time Limit is Expired.";
            else
                textBox3.Text = "The Key Check Failed and the Error Code will Explain why.";

            // Display the full Error Code in Hexadecimal.
            string temp = scb.err_code.ToString("X4") +
                "-" + scb.err_status1.ToString("X4") +
                "-" + scb.err_status2.ToString("X4");
            textBox4.Text = temp;

            // If there was any Error besides "Expired", we don't need
            //   to show the Key's Information since it's probably Invalid.
            //
            // So, only show the Key's Information if the Error Code is:
            //        0 - The Key was Found and is Not Expired.
            //   0x6142 - The Key was Found but the Uses Limit is Expired.
            //   0x6143 - The Key was Found but the Time Limit is Expired.
            if ((scb.err_code != 0) && (scb.err_code != 0x6142) && (scb.err_code != 0x6143))
                return;

            // Display the Product Serial Number.
            temp = (scb.prod_serial).ToString();
            textBox5.Text = temp;

            // Display the current Uses Limit.
            if (scb.err_code == 0x6142)
                temp = "Expired";
            else if (scb.uses_limit == -1)
                temp = "Unlimited";
            else if (scb.uses_limit == 0)
                temp = "On Last Use";
            else
                temp = (scb.uses_limit).ToString();
            textBox6.Text = temp;

            // Display the current Time Limit.
            if (scb.err_code == 0x6143)
                temp = "Expired";
            else if (scb.time_limit == 0xFFFF)
                temp = "Unlimited";
            else if (scb.time_limit == 0)
                temp = "On Last Day";
            else
                temp = (scb.time_limit).ToString() + " Day(s)";
            textBox7.Text = temp;

            // Display the Expire-Date (Local Time Zone, Unicode).
            textBox8.Text = Marshal.PtrToStringUni((IntPtr)scb.expire_date, 16);

            // Display the Expire-Time (Local Time Zone, Unicode).
            textBox9.Text = Marshal.PtrToStringUni((IntPtr)scb.expire_time, 16);

            // Display the User-Data.
            temp = scb.user_data[0].ToString("X2") +
                " " + scb.user_data[1].ToString("X2") +
                " " + scb.user_data[2].ToString("X2") +
                " " + scb.user_data[3].ToString("X2") +
                " " + scb.user_data[4].ToString("X2") +
                " " + scb.user_data[5].ToString("X2") +
                " " + scb.user_data[6].ToString("X2") +
                " " + scb.user_data[7].ToString("X2") +
                " " + scb.user_data[8].ToString("X2") +
                " " + scb.user_data[9].ToString("X2") +
                " " + scb.user_data[10].ToString("X2") +
                " " + scb.user_data[11].ToString("X2") +
                " " + scb.user_data[12].ToString("X2") +
                " " + scb.user_data[13].ToString("X2") +
                " " + scb.user_data[14].ToString("X2") +
                " " + scb.user_data[15].ToString("X2");
            textBox10.Text = temp;

            // Display the Feature-Flags.
            if (scb.feature_flags == 0)
                temp = "None ";
            else
            {
                // Build a string showing all Feature-Flags that are set ON.
                temp = "";                          // Start with empty string.
                UInt16 ff_test_bit = 0x0001;        // Setup to test 1st FF.
                for (int j = 0; j < 16; j++)
                {
                    if ((ff_test_bit & scb.feature_flags) != 0) // If this FF is ON,
                        temp = temp + (j + 1).ToString() + " "; //   add it to string.
                    ff_test_bit <<= 1;                          // Setup to test next FF.
                }
            }
            // Also show the Feature-Flags Word Field (Hex).
            temp = temp + "(0x" + scb.feature_flags.ToString("X4") + ")";
            textBox11.Text = temp;

            // Display the Lasered-ID.
            temp = scb.lasered_id[0].ToString("X2") +
                " " + scb.lasered_id[1].ToString("X2") +
                " " + scb.lasered_id[2].ToString("X2") +
                " " + scb.lasered_id[3].ToString("X2") +
                " " + scb.lasered_id[4].ToString("X2") +
                " " + scb.lasered_id[5].ToString("X2") +
                " " + scb.lasered_id[6].ToString("X2") +
                " " + scb.lasered_id[7].ToString("X2");
            textBox12.Text = temp;

            // Display the Key Type of Device determined by the Lasered-ID.
            if (scb.lasered_id[0] == 0xE1)
                temp = "Rockey4";
            else if (scb.lasered_id[0] == 0xE2)
                temp = "Rockey4ND";
            else
                temp = "Unknown";
            textBox13.Text = temp;

            // Display the Registered Name (Unicode).
            textBox14.Text = Marshal.PtrToStringUni((IntPtr)scb.reg_name, 32);

            // Display the Registered Company (Unicode).
            textBox15.Text = Marshal.PtrToStringUni((IntPtr)scb.reg_company, 32);

            // Display the Registered Other Info (Unicode).
            textBox16.Text = Marshal.PtrToStringUni((IntPtr)scb.reg_other, 32);
        }

    }
}
