Note on C# used in the project development

Part 1

Call Unmanaged Code. Part 1 - Simple DLLImport
By
Vyacheslav Biktagirov

Managed world is beautiful, I have all classes I want in FrameWork.. But what if I want call some unmanaged code? For instance, I have DLL written in C++, and want use it from C#.

Let's look some code. Our DLL exports some function, in CDecl convention, that sums two integers:

extern "C" __declspec(dllexport) __cdecl int sum(int a,int b);

And, of course, we want reuse this code in C#. We must recall, that it is no "direct" way to call unmanaged code, but we must inform the compiler, what we want to call, how, and where is needed code located.

[DllImport("TestDll.dll", EntryPoint="sum",
ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
static extern int sum(int a,int b);

and now we can call it like normal C# function.

x=5;
y=7;
z=sum(x,y);                    // x will receive 12

Here is full C# client code - tested for Beta2.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
 
namespace WindowsApplication6
{
/// 
/// Summary description for Form1.
/// 
public class Form1 : System.Windows.Forms.Form
{
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.TextBox textBox2;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox textBox3;
        /// 
        /// Required designer variable.
        /// 
        private System.ComponentModel.Container components = null;
               public Form1()
        {
               //
               // Required for Windows Form Designer support
               //
               InitializeComponent();
        //
        // TODO: Add any constructor code after InitializeComponent all
        //
}
 
/// 
        /// Clean up any resources being used.
        /// 
        protected override void Dispose( bool disposing )
        {
               if( disposing )
               {
                       if (components != null)
                       {
                               components.Dispose();
                       }
               }
               base.Dispose( disposing );
        }
               #region Windows Form Designer generated code
        /// 
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// 
        private void InitializeComponent()
        {
               this.button1 = new System.Windows.Forms.Button();
               this.textBox1 = new System.Windows.Forms.TextBox();
               this.label1 = new System.Windows.Forms.Label();
               this.textBox2 = new System.Windows.Forms.TextBox();
               this.label2 = new System.Windows.Forms.Label();
               this.textBox3 = new System.Windows.Forms.TextBox();
               this.SuspendLayout();
               //
               // button1
               //
               this.button1.Location = new System.Drawing.Point(64, 192);
               this.button1.Name = "button1";
               this.button1.Size = new System.Drawing.Size(144, 64);
               this.button1.TabIndex = 0;
               this.button1.Text = "call sum";
               this.button1.Click += new System.EventHandler(this.button1_Click);
               //
               // textBox1
               //
               this.textBox1.Location = new System.Drawing.Point(40, 120);
               this.textBox1.Name = "textBox1";
               this.textBox1.Size = new System.Drawing.Size(72, 22);
               this.textBox1.TabIndex = 1;
               this.textBox1.Text = "2";
               //
               // label1
               //
               this.label1.Location = new System.Drawing.Point(128, 128);
               this.label1.Name = "label1";
               this.label1.Size = new System.Drawing.Size(16, 16);
               this.label1.TabIndex = 2;
               this.label1.Text = "+";
               //
               // textBox2
               //
               this.textBox2.Location = new System.Drawing.Point(152, 120);
               this.textBox2.Name = "textBox2";
               this.textBox2.Size = new System.Drawing.Size(56, 22);
               this.textBox2.TabIndex = 3;
               this.textBox2.Text = "3";
               //
               // label2
               //
               this.label2.Location = new System.Drawing.Point(224, 120);
               this.label2.Name = "label2";
               this.label2.Size = new System.Drawing.Size(24, 23);
               this.label2.TabIndex = 4;
               this.label2.Text = "=";
               //
               // textBox3
               //
               this.textBox3.Location = new System.Drawing.Point(248, 120);
               this.textBox3.Name = "textBox3";
               this.textBox3.Size = new System.Drawing.Size(112, 22);
               this.textBox3.TabIndex = 5;
               this.textBox3.Text = "5";
               //
               // Form1
               //
               this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
               this.ClientSize = new System.Drawing.Size(576, 322);
               this.Controls.AddRange(new System.Windows.Forms.Control[] this.textBox3,this.label2,this.textBox2,this.label1,this.textBox1,this.buttn1});
               this.Name = "Form1";
               this.Text = "Form1";
               this.ResumeLayout(false);
               }
        #endregion
               /// 
        /// The main entry point for the application.
        /// 
        [STAThread]
        static void Main()
        {
               Application.Run(new Form1());
        }
               #region My Code
        #region Dll Imports
        [DllImport("TestDll.dll", EntryPoint="sum",
        ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
        static extern int sum(int a,int b);
        #endregion
        #region Button Click Events
        private void button1_Click(object sender, System.EventArgs e)
        {
        textBox3.Text=(int.Parse(textBox1.Text)+int.Parse(textBox2.Text)).ToString();
        }
        #endregion
        #endregion
        
}

It sounds ery simple, becouse "int" is isomorphic type, says, int in C# and ind C++ is identical. What we can do, when we want operate non-isomorhic types, like String? Recall, that .NET string is some Class, while C++ string is char*,or wchar_t*,or BSTR, .. String may be embedded in a structure, or pointed by pointer, or even something more exotic. Let's call some string function.

[DllImport("Advapi32.dll", EntryPoint="GetUserName", ExactSpelling=false,
                        SetLastError=true)]
               static extern bool GetUserName(
                       [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
                       [MarshalAs(UnmanagedType.LPArray)] Int32[] nSize );

This function receives two parameters: char* and int*. Becouse we must allocate char* buffer and receive string by pointer, we can't use UnmanagedType.LPStr attribute, so we pass ANSI string as byte array. int* is more simple-it's 1-element Int32 array. Let's call it:

               private void button2_Click(object sender, System.EventArgs e)
               {
                       byte[] str=new byte[20];
                       Int32[] len=new Int32[1];
                       len[0]=20;
                       GetUserName(str,len);
                       MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));
               }

We allocate 20 bytes for receiving ANSI string,one element in Int32 array, set 20 as max string length and call it. For receiving string from byte array I used Text.Encoding.ASCII class.

That's enough for first part. Second part will speak about more complex interop.

 

Part 2

System.IntPtr Structure

Assembly: Mscorlib.dll
Namespace: System

Summary

A platform-specific type that is used to represent a pointer or a handle.

C# Syntax:

[Serializable]
public struct IntPtr : ISerializable

Thread Safety

This type is safe for multithreaded operations.

Remarks

The IntPtr type is designed to be an integer whose size is platform-specific. That is, an instance of this type is expected to be 32-bits on 32-bit hardware and operating systems, and 64-bits on 64-bit hardware and operating systems.

The IntPtr type can be used by languages that support pointers, and as a common means of referring to data between languages that do and do not support pointers.

IntPtr objects can also be used to hold handles. For example, instances of IntPtr are used extensively in the FileStream class to hold file handles.

The IntPtr type is CLS-compliant, while the UIntPtr type is not. Only the IntPtr type is used in the common language runtime. The UIntPtr type is provided mostly to maintain architectural symmetry with the IntPtr type.

This type implements the ISerializable interface.

System.IntPtr Member Details

Overloaded ctor #1

Summary

Initializes a new instance of IntPtr using the specified 32-bit pointer or handle.

C# Syntax:

public IntPtr(
   int value
);

Parameters:

value

A pointer or handle contained in a 32-bit signed integer.

Return to top


Overloaded ctor #2

Summary

Initializes a new instance of IntPtr using the specified 64-bit pointer.

C# Syntax:

public IntPtr(
   long value
);

Parameters:

value

A pointer or handle contained in a 64-bit signed integer.

Exceptions

Exception Type

Condition

OverflowException

On a 32-bit platform, value is too large to represent as an IntPtr.

Remarks

An exception is only thrown if the value of value requires more bits than the current platform supports.

Return to top


Overloaded ctor #3

Summary

Initializes a new instance of IntPtr using the specified pointer to an unspecified type.

This member is not CLS Compliant

C# Syntax:

[CLSCompliant(false)]
unsafe public IntPtr(
   
void* value
);

Parameters:

value

A pointer to an unspecified type.

See also:
Void

 

Part 4

 

.NET Framework Class Library

 

Marshal.PtrToStringUni Method

Allocates a managed String and copies a specified number of characters from an unmanaged Unicode string into it.

Overload List

Allocates a managed String and copies all characters up to the first null character from an unmanaged Unicode string into it.

Supported by the .NET Compact Framework.

 

 

 

Name Description Marshal.PtrToStringUni (IntPtr) Allocates a managed String and copies all characters up to the first null character from an unmanaged Unicode string into it.

Supported by the .NET Compact Framework.

Marshal.PtrToStringUni (IntPtr, Int32) Copies a specified number of characters from a Unicode string stored in native heap to a managed String.

Supported by the .NET Compact Framework.

 

Funtion Overloading in C#

Function Overloading

a.cs

class zzz

{

public static void Main()

{

yyy a = new yyy();

a.abc(10);

a.abc("bye");

a.abc("no",100);

}

}

class yyy

{

public void abc(int i)

{

System.Console.WriteLine("abc" + i);

}

public void abc(string i)

{

System.Console.WriteLine("abc" + i);

}

public void abc(string i,int j)

{

System.Console.WriteLine("abc" + i + j);

}

}

Output

abc10

abcbye

abcno100

The class yyy has three functions, all of them having the same name abc. The distinction between them is in the data types of the parameters. They are all different. In C# we are allowed to have functions with the same name, but having different data types parameters. The advantage is that we call the function by the same name as by passing different parameters, a different function gets called. This feature is called function overloading. All is fine only if the parameter types to the function are different. We do not have to remember a large number of functions by name.

The only reason why function overloading works is that C# does not know a function by name, but by its signature. A signature denotes the full name of the function. Thus the name of a function or its signature is the original function name plus the number and data types of its individual parameters.

a.cs

class zzz

{

public void abc()

{

}

public int abc()

{

}

public static void  Main()

{

}

}

Compiler Error

a.cs(6,12): error CS0111: Class 'zzz' already defines a member called 'abc' with the same parameter types

Here we have two functions abc which differ only in the values they return. As return values do not count in the function signature and the function names are similar, hence the error.

a.cs

class zzz

{

static void abc(int i)

{

}

public void abc(int i)

{

}

public void abc(string p)

{

}

public static void  Main()

{

}

}

Compiler error

a.cs(6,14): error CS0111: Class 'zzz' already defines a member called 'abc' with the same parameter types

We have 2 abc's, that accept an int and differ only in the addition of a modifier static. They have the same signature as modifiers like static are not considered as part of the function signature. Also, in the next program, we have two abc's with different access modifiers which differ in the parameters, hence signature/name changes causing an error.

a.cs

class zzz

{

void abc(int i)

{

}

void abc( out int i)

{

i = 10;

}

void abc( ref int i)

{

}

public static void  Main()

{

}

}

Compiler Error

Microsoft (R) Visual C# Compiler Version 7.00.9254 [CLR version v1.0.2914]

Copyright (C) Microsoft Corp 2000-2001. All rights reserved.

a.cs(10,6): error CS0663: 'abc' cannot define overloaded methods which differ only on ref and out

a.cs(6,6): (Location of symbol related to previous error)

The signature consists of not only the parameter data types, but also the kind of parameters i.e. out ref etc. As function abc takes an int with different modifiers i.e. out etc, the signature on each is different. The signature of a method consists of its name and number and types of its formal parameters. The return type of a function is not part of the signature. No two functions can have the same signature and also non-members cannot have the same name as members. 

A function/method can be called by four different types of parameters. These are pass by value, reference, output and finally parameter arrays. The parameter modifier is not part of the function signature. Lets now understand what parameter arrays are all about.

 


 

Part 5

LocalFree

This function frees the specified local memory object and invalidates its handle.

HLOCAL LocalFree(
  HLOCAL hMem 
); 

Parameters

hMem

Handle to the local memory object. This handle is returned by either the LocalAlloc or LocalReAlloc function.

Return Values

NULL indicates success. A handle to the local memory object indicates failure. To get extended error information, call GetLastError.

Remarks

If the process tries to examine or modify the memory after it has been freed, heap corruption may occur or an access violation exception (EXCEPTION_ACCESS_VIOLATION) may be generated.

If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL.

The LocalFree function will free a locked memory object. A locked memory object has a lock count greater than zero.

If an application is running under a debug version of the system, LocalFree issues a message that tells you that a locked object is being freed. If you are debugging the application, LocalFree enters a breakpoint just before freeing a locked object. This allows you to verify the intended behavior and then continue execution.

For Windows CE versions 1.0 through 2.12, allocating memory approximately 0 to 7 bytes under 192K in size causes the corresponding call to LocalFree to fail for certain memory blocks in this size range. The return code is ERROR_INVALID_PARAMETER.

Requirements

OS Versions: Windows CE 1.0 and later.
Header: Winbase.h.
Link Library: Coredll.lib.

See Also

LocalAlloc | LocalReAlloc

Using the InputPanel Control in Your Pocket PC Applications

 

Christian Forsberg
businessanyplace.net

September 2003

Applies to:
   Microsoft® .NET Compact Framework version 1.0
   Microsoft Visual Studio® .NET 2003
   Windows Mobile-based Pocket PCs
   Windows Mobile 2003 Second Edition software for Pocket PCs

Summary: Learn how the InputPanel control can be used to create an efficient user interface in your Pocket PC applications. This article will explain why it is important and show you how it can be done. (5 printed pages)

Download inputpanel_control.exe

Contents

Why SIP Support?
The InputPanel Control
InputPanel Sample
Code Walkthrough
Conclusion

Why SIP Support?

Most Windows Mobile-based Pocket PC applications require input from the user, and the usual way to enter data is the Soft Input Panel (SIP), which supports several input methods. The standard SIP enables you to select characters from a virtual keyboard or write freehand characters and even words that are recognized. There are also a number of third-party products available for other input methods (for example, using the full screen as a virtual keyboard that can be manipulated with your fingers).

As many mobile applications require input when being used in the field, it is important that your applications take the SIP into consideration. The first consequence of using the SIP is that it will partially cover your application's forms when visible. If your user interface does not react to the SIP, it means that some of your controls may be hidden, and therefore input into these controls is not possible. Users are not comfortable about managing the visibility of the SIP manually. They expect the SIP to be made visible when an input field is selected.

The InputPanel Control

The InputPanel control included with Microsoft® Visual Studio® .NET 2003 is the way to interface with the SIP on a Pocket PC. The implementation of the control is straightforward, with just one property (Enabled) that controls the visibility of the SIP and one event (EnabledChanged) that is fired when the visibility of the SIP changes.

Let's see how the InputPanel control can be used in a sample Pocket PC application.

InputPanel Sample

This is a sample application for the Pocket PC created with Visual Studio .NET 2003, C#, and the Microsoft .NET Compact Framework. It shows how to implement the InputPanel control in a tabbed form and consists of one form:

Figure 1. InputPanel sample

The form has a tab control with two pages holding a number of input fields. When a TextBox control receives the focus, the SIP is enabled (shown). When the TextBox looses the focus the SIP is disabled (hidden). A very important detail is that the tab control is resized when the SIP is shown to enable the user to select different tabs even when the SIP is enabled.

The design of the form has taken the SIP into consideration because no controls are hidden when the SIP is enabled. A special case is the ComboBox control that does not require any input, and therefore doesnÂ’t require the SIP. To maximize the use of the form space, such controls can be placed in the lower area of the form, as the SIP will be removed when the control receives the focus:

Figure 2. SIP removed when ComboBox has focus

As the above figure shows, the SIP is removed when the ComboBox has the focus. However, if possible, a perfect design should allow all controls to be fully visible when the SIP is shown.

Now, let's take a look at how this form is implemented.

Code Walkthrough

In the sample code, the InputPanel control is named inpSIP. To enable each of the TextBox controls to enable (show) and disable (hide) the SIP control, depending on if they have the focus, the following event handlers are created:

private void txt_GotFocus(object sender, System.EventArgs e)
{
  inpSIP.Enabled = true;
}
private void txt_LostFocus(object sender, System.EventArgs e)
{
  inpSIP.Enabled = false;
}

As they are very generic, the same event handlers can be attached to all the TextBox controls in the form. When attaching the event handlers to each TextBox, the code generated looks like this:

txtFirst.GotFocus += new System.EventHandler(this.txt_GotFocus);
txtFirst.LostFocus += new System.EventHandler(this.txt_LostFocus);
txtSecond.GotFocus += new System.EventHandler(this.txt_GotFocus);
txtSecond.LostFocus += new System.EventHandler(this.txt_LostFocus);
// and so on

The only issue with this approach is when you close the form, the Enabled property of the InputPanel control may not be available when the LostFocus event is fired on the TextBox that has the focus. One simple solution to handle this is to set the focus to a non-TextBox control in the Closing event of the form:

private void frmMain_Closing(object sender, CancelEventArgs e)
{
  cboSixth.Focus();
}

In this case the focus is set to the ComboBox control when the form is closed.

The final issue to solve is the resizing of the TabControl when the SIP is enabled (visible). The code to do this looks like this:

private void inpSIP_EnabledChanged(object sender, System.EventArgs e)
{
  if (inpSIP.Enabled)
  {
    tabControl.Height = 246 - inpSIP.Bounds.Height;
  }
  else
  {
    tabControl.Height = 246;
  }
}

The EnabledChanged event of the InputPanel control is fired each time the SIP state (enabled/disabled) changes. This enables us to change the size of the TabControl control. The Bounds property of the InputPanel control holds the rectangle of the SIP when it is visible.

Conclusion

With simple code, your user interface can handle the SIP in an efficient way using the InputPanel control. Your code can support showing the SIP control when required (when TextBox controls have focus). It can also handle the dynamic changes to the user interface (resize of TabControl controls) with a minimum of code.

 

 

 

 

 

 

 

 

 

 

Introduction

INI files and the registry are generally things of the past for .NET applications. But what to use? XML seems appropriate, but one look at System.XML is enough to scare most developers off, especially just to store a few fields. Fortunately, there is a very easy way in .NET to solve this, but one that is usually not seen by developers. In this article, I’ll explain how you can easily store and retrieve your application settings with just a few lines of code.

History

In Windows 3.1, developers used INI files to store settings. In general, they worked pretty well for simple settings, but were not appropriate for more complex data. INI files also did not account for multiple users, and thus Microsoft invented the registry.

Along came the registry with Win32. The registry was fast, hierarchical, multi user, and allowed storage of typed data. But unfortunately, the registry was a central system component and was not contained as part of the application install.

Next, XML became popular. XML offers fast, hierarchical storage of typed data. However, XML is so flexible that for most users doing anything simple is quite an undertaking. Fortunately, there are easier ways than using System.XML and handling everything yourself.

Old Ways

Many users have simply resorted to using INI files or the registry. INI files are not supported in .NET. To use INI files, a developer must call the Win32 API directly, or use some prepared classes on the Internet that use the Win32 API. For the registry, classes are available in Microsoft.Win32. XML however is portable and can be easily edited by end users if necessary.

How

The secret to painless XML settings files is to use a typed DataSet. A typed DataSet is actually an in memory dataset for working with ADO.NET, but they have many other uses as well. To add a typed DataSet to your application, right click on the project in the Solution Explorer, and select Add New Item. Now select DataSet, and give the dataset a name.

Now, we have a blank DataSet in our project. For the purposes of a demo, I have created a main form already that looks like this.

I have chosen these for the demo as they give us three types of data to store, a string, an integer, and a Boolean. Now, let’s design our DataSet around these.

Open the Solution Explorer and find the newly created DataSet.

When you double click on Settings.xsd, you will see a designer like this:

This is a blank DataSet. A DataSet can contain several tables, but for this demo, we will add just one. Open the toolbox, and you will see different items than you normally see in a WinForms or a WebForms application.

There are a lot of items in the toolbox related to DataSets, but for the needs of this article, we only need Element. Double click on Element to add one to the DataSet. The DataSet should now look like this:

The Element type corresponds to a DataTable. Let’s name it Main (change the highlighted text above to Main). Now, let’s enter the fields that we want to store. The element should look like this when finished:

Visual Studio will now take this DataSet and make a set of classes for us that we can use. So now, let’s take a look at the Load and Save buttons on the main form. These events make use of ConfigPathname, this is a field that is predefined in the demo. ConfigPathname just holds the path and filename of the settings file.

C#

private void butnSave_Click(object sender, System.EventArgs e) {
    Settings xDS = new Settings();
    Settings.MainRow xRow = xDS.Main.NewMainRow();
    xRow.Username = textUsername.Text.Trim();
    xRow.PIN = int.Parse(textPIN.Text.Trim());
    xRow.Admin = chckAdmin.Checked;
    xDS.Main.AddMainRow(xRow);
    xDS.WriteXml(ConfigPathname, System.Data.XmlWriteMode.IgnoreSchema);
}
 
private void butnLoad_Click(object sender, System.EventArgs e) {
    if (new FileInfo(ConfigPathname).Exists) {
        Settings xDS = new Settings();
        xDS.ReadXml(ConfigPathname, System.Data.XmlReadMode.IgnoreSchema);
        if (xDS.Main.Rows.Count > 0) {
            Settings.MainRow xRow = xDS.Main[0];
            if (!xRow.IsUsernameNull()) {
                textUsername.Text = xRow.Username;
            }
            if (!xRow.IsPINNull()) {
                textPIN.Text = xRow.PIN.ToString();
            }
            if (!xRow.IsAdminNull()) {
                chckAdmin.Checked = xRow.Admin;
            }
        }
    }
}

Visual Basic.NET

Private Sub butnSave_Click(ByVal sender As System.Object, 
ByVal e As System.EventArgs) Handles butnSave.Click
  Dim xDS As New Settings
  Dim xRow As Settings.MainRow
  xRow = xDS.Main.NewMainRow
  xRow.Username = textUsername.Text.Trim()
  xRow.PIN = Int32.Parse(textPIN.Text.Trim())
  xRow.Admin = chckAdmin.Checked
  xDS.Main.AddMainRow(xRow)
  xDS.WriteXml(ConfigPathname, System.Data.XmlWriteMode.IgnoreSchema)
End Sub
 
Private Sub butnLoad_Click(ByVal sender As System.Object, 
ByVal e As System.EventArgs) Handles butnLoad.Click
  If New FileInfo(ConfigPathname).Exists Then
    Dim xDS As New Settings
    Dim xRow As Settings.MainRow
    xDS.ReadXml(ConfigPathname, System.Data.XmlReadMode.IgnoreSchema)
    If xDS.Main.Rows.Count > 0 Then
      xRow = xDS.Main.Rows.Item(0)
      If Not xRow.IsUsernameNull() Then
        textUsername.Text = xRow.Username
      End If
      If Not xRow.IsPINNull() Then
        textPIN.Text = xRow.PIN.ToString()
      End If
      If Not xRow.IsAdminNull() Then
        chckAdmin.Checked = xRow.Admin
      End If
    End If
  End If
End Sub

When loading the DataSet back, it is necessary to check each field for null. In our demo, they would never be null, but if you later add fields and your application tries to load a file that was saved with an older version, fields could be null. End users might also directly edit your settings files. Accessing a field while it is null will generate an exception.

If you run the demo, you can enter some test values and then click Save.

After clicking Save, the demo will create a .settings file in the same directory as the .exe. Normally, this is bin/debug/SettingsDemo.exe.Settings. If you open this file, you will see it is a standard XML file and can easily be edited.

<?xml version="1.0" standalone="yes"?>
<Settings xmlns="http://tempuri.org/Settings.xsd">
  <Main>
    <Username>Kudzu</Username>
    <PIN>1234</PIN>
    <Admin>true</Admin>
  </Main>
</Settings>

Now, if you run the application again, you can click the Load button to load these settings.

In this demo, we only stored one row in the DataTable. But DataTables can contain multiple rows, and a DataSet can even contain multiple related or unrelated DataTables.

Conclusion

XML files are a widespread standard that allows easy storage of structured typed data. Use of XML files allows easy editing by end users, and even by other software. Using typed DataSets, you can easily store your settings in XML files.

About Chad Z. Hower aka Kudzu

 

Hosted by www.Geocities.ws

1