SECURITY
========
CRYPTOGRAPHY
============
HASHING:
========
Hashing is good for storing passwords into databases. Hashing does not actually encrypt the
password string in a way which allows us to decrypt it back. It only collects information
about the string and stores the information into a preset number of bits which can be as small
as 16 bits regardless to the string size.
So, how are we going to be using the hashed password if we'll never be able to decrypt it back?
The answer is that each time someone sends us a password we'll hash it using the same algorithm,
then compare the two hashed passwords to see if they are identical. This should be good enough
for authentication while improving security since the actual password is not stored into our
system. If someone could find his way to the database, he will know the hash code, but this will
not get him the password.
The algorithms which we use are MD5, SHA1, SHA256 and SHA512. The number of bits used to store the
hash data are (in order) 16, 20, 32 and 64.
SYMMETRIC ENCRYPTION:
=====================
Symmetric encryption means that encryption and decryption use the same key. The algorithm which
we use for this type of encryption is called "Rijndael" algorithm. You don't have to remember
this name since it's the only type we use.
It uses two keys which are used together to do both the encryption and the decryption. Method
tm() can get random keys for you. They come assigned to the public byte arrays JY[] and KY[].
JY[] alone is called the encryption key while (KY[]) is called "Initialization Vector".
Symmetric encryption is fast compared to asymmetric one. This makes it the right choice when
we encrypt larger amount of data.
===============================================================================================
Example 1: If the original password was "Apple", show how it could be encrypted using hashing
and symmetric encryption methods. Then, if the owner of the password logs-in using the password,
show how his password will be checked against the original in both cases. Also display the keys
used and the encrypted data of both methods.
===============================================================================================
public class a : pcs {
public override void init() {
tia=toa="t";
base.init();
}
public override void run() {
//------------------------------------ Hashing ------------------------------------------
cls="r0";
os=" HASHING";tm();
os="Apple"; // Original password.
js="sha256";tm("Eh"); // Hash it using "SHA256" Algorithm.
cls="b0";tm();xs=os; // Display encrypted string then save it.
os="Apple"; // Someone logged-in using this password.
js="sha256";tm("Eh");tm(); // Hash the received password
if (xs==os) { // Compare the two hash codes
os="The two strings are identical."; // If identical, this message
cls="G4";tm(); // is displayed.
}
cls="S9";
os="=======================================================================================";
tm();cls="r0";
//------------------------------- Symmetric Encryption ----------------------------------
os=" SYMMETRIC ENCRYPTION";tm();
os="Apple";xs=os; // Original Password. Save it temporarely
cls="b0";os="Original String: "+os;tm(); // Display original string
tm("Esk"); // Obtain key, initialization vector (JY,KY)
//---- Displaying Keys ------
cls="S9"; // Change color to black:
os="";tm();os="KEY: ";tm("d"); // Display the key
for (n=0;n< JY.Length;n++) {os=" "+JY[n];tm("d");}
os="";tm();os="IV : ";tm("d"); // Display the IV
for (n=0;n< KY.Length;n++) {os=" "+KY[n];tm("d");}
os="";tm();tm(); // Skip a line
//---- Encryption & Decryption -----
os=xs;tm("Ese");es=os; // Encrypt string, save result.
os="Encrypted String: "+os;tm(); // Display encrypted string
os=es;tm("Esd");ys=os; // Decrypt string back. Save result
cls="b0";os="";tm(); // Skip one line.
os="Decrypted String: "+ys;tm(); // Display decrypted string
}
}
===============================================================================================

===============================================================================================
ASYMMETRIC ENCRYPTION:
======================
This is also called "Public Key Encryption". Two keys are used for this type of encryption,
a public key which is given to other people and a private key which the owner keeps for himself.
The two keys work together in a unique way. If you encrypt data with one of them, you can
decrypt it with the other one. Typically we encrypt with the public key and decrypt with the
private one, however when we digitally sign a document we do it the other way.
The asymmetric encryption algorithm which we use is "RSA".
Modes of method tm() which handle asymmetric encryption:
--------------------------------------------------------
Eaf: Generate a new key pair and store it into an XML file.
Eak: Generate a new key pair and store it into a secure container.
Eae: Encrypt data.
Ead: Decrypt data.
Eas: Digitally sign data.
Eav: Verify a digital signature.
Eac: Clear (dispose) RSA Provider object (necessary for security)
Obtaining a key pair and storing it:
------------------------------------
Asymmetric private keys should never be stored in plain text on the local computer. If you need
to store a private key, you should use a key container. You should select a name for the
container which stores each key pair.
To generate new keys and store them into a new container, assign the container name to (ks) and
call tm("Eak") Additionally, this method returns to you (os) containing the public keys in XML
format. If you make the assignment (ib=true) before calling the method, (os) will contain both
keys. You may store that string into a file with the ".xml" extension (Using tm("Eaf") or you may
send it to someone across the web (Make sure it contains your public key only in this case) The
person who receives it will be able to encrypt documents with it which you are the only one who
can decrypt. We will return to that in Example 4.
Using a key pair which is stored into a container:
--------------------------------------------------
Assign existing container name to (ks) before calling tm("Eae"), tm("Ead"), tm("Eas") or
tm("Eav") If the name in (ks) matches an existing container name, the keys it contains will
be used for the operations.
If you call tm("Eak") with an existing container name, nothing will be done other than assigning
either the public key or both keys to (os) depending on the value of (ib) as explained before.
Deleting a container and destroying the key pair which it contains:
-------------------------------------------------------------------
Make the assignment (kb=true;) and call method tm("Eak") with the container name.
Encrypting and decrypting:
--------------------------
Encryption is done using the public key while decryption is done using the private key. You call
method tm("Eae") for encryption and method tm("Ead") for decription. Both methods expect the key
container name in (ks) and the data either assigned to (os) or assigned to OY[]. If you like to
supply the data in OY[] you must keep os=""or ="@" which means "Use data of byte array OY[]".
One more parameter which you may supply when you encrypt data is (jb) Set (jb=true) if you
like to use "OAEP padding" for the encryption which is possible only for Windows XP and later
versions.
The output of both methods comes assigned to both (os) and OY[]. The string output of encrypted
data is always of base64 type.
DIGITAL SIGNATURES:
===================
Whenever someone sends you an important document, even if there was no fear of sending the
document across the web, you may suspect that the document could be a fake one sent to you
by someone other than the person you know. This is what makes digital signatures useful.
To sign the document, the person who sent it to you needs to create a hash code for the document
and encrypt it with his private key. On the other end, you decrypt his hash code with your public
key and compare it with another hash code which you create for the document which you have
received.
To sign data, use method tm("Eas") and to verify a signature use method tm("Eav") Both methods
expect the key container name in (ks) and the data either assigned to (os) or assigned to OY[].
If you like to supply the data in OY[] you must keep os="" or ="@". Both methods also expect to
receive the hashing algorithm name assigned to (js) Only two algorithms are used for signing
which are MD5 and SHA1.
Method tm("Eas") outputs the "hashed and encrypted" data (the signature) in OY[] and also in the
base64 string (os)
Method tm("Eav") expects to receive the signature assigned to IY[] since OY[] will be used for
the data. This verification method outputs (ob) which is assigned (true) if the signature was
found to be valid and (false) if it was found to be invalid.
===============================================================================================
Example 2: If the original password was "Apple", show how it could be encrypted using asymmetric
encryption method. Show also how to sign data and how to verify a digital signature.
===============================================================================================
public class a : pcs {
public override void init() {
tia=toa="t";
base.init();
}
public override void run() {
cls="r0";
//------------------------------- Asymmetric Encryption ----------------------------------
os=" ASYMMETRIC ENCRYPTION";tm();
os="Apple";xs=os; // Original Password. Save it temporarely
cls="b0";os="Original String: "+os;tm(); // Display original string
ks="MyContainer";tm("Eak"); // Obtain a key pair, store it into "MyContainer"
//---- Encryption & Decryption -----
os=xs;ks="MyContainer";tm("Eae");es=os; // Encrypt string (using keys in container),
cls="S9"; // save result. Change color.
os="Encrypted String: "+es;tm(); // Display encrypted string
os=es;ks="MyContainer";tm("Ead");ys=os; // Decrypt string back. Save result
cls="b0"; // Change color.
os="Decrypted String: "+ys;tm(); // Display decrypted string
ks="MyContainer";tm("Eac"); // Clear RSA object.
cls="S9";
os="======================================================================================";
tm();cls="r0";
os=" DIGITAL SIGNATURES";tm();
//---------------------------------- Digital Signing --------------------------------------
os="Data";xs=os; // Original data. Save it temporarely
cls="b0";os="Original String: "+os;tm(); // Display original string
ks="MyContainer";js="MD5";os=xs;tm("Eas"); // Hash then encrypt with private key using
es=os; // key pair in "MyContainer". Save result.
// REMARK:OY[] contains a 2nd copy of result in
cls="S9"; // binary form. Change color.
os="Encrypted String: "+es;tm(); // Display encrypted string
// ** Original data in (xs) and Encrypted hash code in OY[] are now being sent across the web.
//------------------------------------ Signature Verification ------------------------------
ks="MyContainer";js="MD5";os=xs;IY=OY; // Call verification mode with key container,
tm("Eav"); // encryption algorithm, original data and
// encrypted signature assigned to IY[].
cls="b0"; // Change color.
os="Signature was found to be: "+ob;tm(); // Display verification result.
ks="MyContainer";kb=true;tm("Eak"); // Delete key pair
ks="MyContainer";tm("Eac"); // Clear RSA object.
}
}
===============================================================================================

===============================================================================================
USING A FILE TO STORE KEYS:
===========================
As we have mentioned before, we don't recommend storing keys into local files. However, we are
going to show you here how to store generated keys into an XML file and how to use the keys
which are stored into the file.
To make the file:
-----------------
Method tm("Eaf") generates a new key-pair and stores the data into a file. It expects the
following parameters:
(1) A file name with the extension "xml" assigned to (fls)
(2) The flag (ib) If you keep (ib=false), the file will only contain the public key. If you
assign (true) to (ib) the file will store the complete pair.
To use the file keys:
---------------------
When you call methods tm("Eae"), tm("Ead"), tm("Eas") or tm("Eav"), Supply the methods with:
ks="*f" and fls="The File Name".
===============================================================================================
Example 3: (a) Show how to generate new key pair and store it into file "keys.xml".
(b) Modify the code of example 2 so that the key pair used for all operations comes
from that file.
Use two different blocks for (a) and (b)
===============================================================================================
public class a : pcs {
public override void init() {
tia=toa="t";
bli=1; // Starting block number.
base.init();
}
public override void run() {
if (blp==1) {
fls="keys.xml";ib=true;tm("Eaf"); // Generate new keys, store into file.
os="A new pair of keys have been generated and stored into file keys.xml";tm();
} // Inform user.
else if (blp==2) {
cls="r0";
//------------------------------- Asymmetric Encryption ----------------------------------
os=" ASYMMETRIC ENCRYPTION";tm();
os="Apple";xs=os; // Original Password. Save it temporarely
cls="b0";os="Original String: "+os;tm(); // Display original string
ks="*f";fls="keys.xml";tm("Eak");cs=os; // Get key pair from file and assign public key
cls="G4"; // to (os) then save (os). Change color.
os="Public Key: "+cs;tm(); // Display public key.
//---- Encryption & Decryption -----
fls="keys.xml"; // Keys' file.
os=xs;ks="*f";tm("Eae");es=os; // Encrypt string (using keys in file),
os=es;ks="*f";tm("Ead");ys=os; // Decrypt using file keys. Save result
cls="b0"; // Change color.
os="Decrypted String: "+ys;tm(); // Display decrypted string
ks="*f";tm("Eac"); // Clear RSA object.
cls="S9";
os="======================================================================================";
tm();cls="r0";
os=" DIGITAL SIGNATURES";tm();
//---------------------------------- Digital Signing ------------------------------------
os="Data";xs=os; // Original data. Save it temporarely
cls="b0";os="Original String: "+os;tm(); // Display original string
js="MD5";os=xs;ks="*f";tm("Eas"); // Hash then encrypt with private key using
es=os; // key pair in file. Save result.
cls="S9"; // Change color.
os="Encrypted String: "+es;tm(); // Display encrypted string
js="MD5";os=xs;IY=OY;ks="*f";tm("Eav"); // Call verification mode with original data,
// encrypted signature assigned to IY[], hash
// algorithm and where to find key pair.
cls="b0"; // Change color.
os="Signature was found to be: "+ob;tm(); // Display verification result.
ks="*f";tm("Eac"); // Clear RSA object.
}
}
}
===============================================================================================
Compile the file with: psp a, run it to generate the file.
Change the start block number statement in method init() with (bli=2). Compile the file again
then run it several time. Notice that the public key is always the same since it comes from
the same file all the time. Notice also that the digital signature is also always the same.
===============================================================================================

===============================================================================================
How to send your public key to others:
--------------------------------------
You know that method tm("Eak") returns XML formatted string into (os) If we supply (ib=false)
which is the default to the method, the string returned will be for the public key only. This
XML formatted string is what you send to others.
How can others encrypt data with your XML-Formatted public key:
---------------------------------------------------------------
They need to make the assignments:
kys = The string they received from you.
ks = "*s"
os or OY[]=Data to be encrypted.
before calling method tm("Eae")
REMARK: Another alternative would be to file the string in (os) with:
fls="MyFile.xml";fm("W");
Then, to encrypt data, (kys) and (ks) above should be replaced with:
fls="MyFile.xml";ks="*f";
Notice that the letter "W" in fm("W") is an upper case one. Notice also that the key
is filed as a string since it's in XML format.
Combining Symmetric with Asymmetric Algorithms:
===============================================
Although asymmetric algorithms are the more secure, they require extensive computations which
makes them slow. They are good only for passwords or short phrases. Symmetric encryption is
suitable for encrypting larger amounts of data.
When exchanging messages accross the internet, symmetric encryption should be used to encrypt
the data while asymmetric encryption should be used to safely transport the symmetric encryption
keys.
Let us consider a two way communication between two parties which requires this kind of
encryption. Here is how it could go:
(1) Party 1 generates asymmetric key-pair and stores it into container "temp". Additionally he
sends the XML formatted public key returned into (os) from method tm("Eak") to party 2.
(2) Party 2 receives the string and assigns it to (kys) He then uses method tm("Esk") to obtain
a new key and IV for the symmetric encryption.
(3) Party 2 encrypts key and IV with the public key he received as described above. The encrypted
keys will then be sent to party 1.
(4) Prty 1 decrypts received sym key and IV with his private key stored into container "temp"
and assigns the decrypted output to JY & KY respectively.
** Now both parties have same symmetrical encryption key and IV assigned to JY[] & KY[] at
their computers. So they can decrypt each other's encrypted messages.
We'll show an example under the chapter of "Networking" to show how this communication can take
place between a client and a server. At this time we'll still show it with the two parties
sharing the same class.
===============================================================================================
Example 4: Show how to use symmetric encryption to encrypt communication between two parties
while asymmetric encryption will be used to encrypt the sym. encryption keys.
===============================================================================================
public class a : pcs {
public override void init() {
tia=toa="t";
base.init();
}
public override void run() {
cls="r0";
os=" TWO WAY COMM INVOLVING SYM AND ASYM ENCRYPTIONS";tm();
// (1) Party 1 generates asymmetric key-pair, stores it into container "temp" and sends
// public key to party 2.
ks="temp";tm("Eak");
// (2) Party 2 receives the string and assigns it to (kys) then obtains new
// key & IV for symmetric encryption
kys=os;tm("Esk");
// (3) Party 2 encrypts each of the key and IV with the public key it received then sends
// them to party 1
OY=JY;os="";ks="*s";tm("Eae");JY=OY;
OY=KY;os="";ks="*s";tm("Eae");KY=OY;
// (4) Prty 1 decrypts received sym key and IV with his private key stored into container
// "temp" and assigns the decrypted output to JY & KY respectively.
OY=JY;os="";ks="temp";tm("Ead");JY=OY;
OY=KY;os="";ks="temp";tm("Ead");KY=OY;
//---------------------------- Displaying decrypted Keys -------------------------------
os="";tm();
os="KEY AND IV AFTER BEING DECRYPTED BY PARTY 1:";tm();
cls="S9";os="KEY: ";tm("d"); // Display the key
for (n=0;n< JY.Length;n++) {os=" "+JY[n];tm("d");}
os="";tm();os="IV : ";tm("d"); // Display the IV
for (n=0;n< KY.Length;n++) {os=" "+KY[n];tm("d");}
os="";tm();tm(); // Skip a line
//---------------------------------------------------------------------------------------
cls="r0";os="EXCHANGING MESSAGES:";tm();cls="b0";
// (5) Prty 1 encrypts his secret message with the sym keys and sends it to party 2
os="This is my secret message.";tm("Ese");
// (6) Party 2 decrypts the message with his sym keys and reads it:
tm("Esd");os="Party 1's message after being decrypted by party 2: "+os;tm();os="";tm();
// (7) Party 2 encrypts his confidential reply with the sym keys and sends it to party 2:
os="And this is my confidential reply.";tm("Ese");
// (8) Party 1 decrypts the message with his sym keys and reads it:
tm("Esd");os="Party 2's reply after being decrypted by party 1: "+os;tm();
}
}

===============================================================================================
PASSWORD BASED ENCRYPTION:
==========================
Additional to all other types of encryption, Windows provides an encryption which is based on
the user password which you login with. Here is how to use this type of encryption:
The method to use for encryption is tm("Epe") and the method to use for decryption is tm("Epd")
The two methods are similar to all other encryption and decryption methods. They expect either
a string (in os) or a byte array (in OY[]) If you like OY[] to be used, you must make the
assignment (os="@") before calling the methods.
Both methods return data in both string and binary formats assigned to both (os) and (OY[])
The string representing encrypted data is always a base 64 string while the string representing
decrypted data is a UTF-8 encoded string.
===============================================================================================
Example 5: Use password-based encryption to encrypt the string "Apple" then decrypted back.
===============================================================================================
public class a : pcs {
public override void init() {
tia=toa="t";
base.init();
}
public override void run() {
os="";tm();tm();cls="r0";
os=" PASSWORD BASED ENCRYPTION";tm();
os="";tm(); // Display title, skip a line.
os="Apple";xs=os; // Original Password. Save it temporarely
cls="b0";os="Original String: "+os;tm(); // Display original string
//---- Encryption & Decryption -----
os=xs;tm("Epe");es=os; // Encrypt string, save result.
cls="S9";os="";tm(); // Change color, skip one line
os="Encrypted String: "+es;tm(); // Display encrypted string
os=es;tm("Epd");ys=os; // Decrypt string back. Save result
cls="b0";os="";tm(); // Change color, skip one line.
os="Decrypted String: "+ys;tm(); // Display decrypted string
}
}
===============================================================================================

===============================================================================================
SIGNED ASSEMBLIES
=================
(For versions 1.75 and higher)
What are assemblies?
====================
All the library, console executable and window executable files which you get when you apply PC#
compiling tools to ".cs" files are simple single file assemblies. They can be copied or deleted
like any file without having to worry about registry entries.
An assembly can be of single module or multiple module. Multiple module assemblies are mainly
necessary when more than one programming language have been used to write their classes. They
are not necessary to us. What we are going to learn here is how to create a more complicated
multiple file, single module assemblies which are signed by their authors.
Assembly's strong name:
=======================
Whenever you create (or set) an assembly, you need to supply method sm("as") with assembly's
identity information in addition to other parameters. Also when you like to know the identity
information of an assembly, you call method sm("agN") which returns to you the same data in the
same order. The data you receive from method sm("agN") comes assigned to array OS[]. Here is
what you get (in order):
(1) Assembly's simple name. We require that this name should be the name of assembly file with
no extension or containing folders. This means that if the file name was "c:\\pcs.exe", the
simple name should be "pcs".
(2) The version mumber. Should be a string which contains 4 integers seperated with dots, like
"1.7.5.0".
(3) The Culture string. It's made of a language 2-char code like "en","de" or "fr", followed
with '-', followed with a language region code like "US", "GB" or "AU". So, if you speak US
English, your culture string should be "en-US".
You can specify culture string for "library" assemblies only. Keep this field empty for
"executable" assemblies.
(4) Company name. If you like to specify it, you can.
(5) Copyright information.
(6) Public key. The assembly contains a digital signature which as you already know is a hash
code encrypted with your private key and requires your public key to verify it with. The
assembly contains both the hash code and your public key.
The public key is part of the assembly's identity. It's verified each time the assembly is
referenced. This is why nobody can replace both your public key and the hash code with his.
(7) Public Key Token. It's the last 8 bytes of the SHA-1 hash of the public key under which the
application or assembly is signed.
Let's have an example before we get further.
===============================================================================================
Example 6: Read and display all identity information of the signed version of assembly
"pcs.exe".
===============================================================================================
public class a : pcs { // Always remember, class name = file name
public override void init() {
toa="t";
base.init();
}
public override void run() {
fls="pcs.exe";sm("agN"); // Get all identity information of the assembly
cls="r0";fns="trb16"; // Display title followed with all information.
os=" Assembly Name Components of "+fls;
tm();os="";tm();fns="trb12";
cls="b0";os="Assembly File Name: ";tm("d");
cls="S9";os=OS[0];tm();//os="";tm();
cls="b0";os="Assembly Version : ";tm("d");
cls="S9";os=OS[1];tm();//os="";tm();
cls="b0";os="Assembly Culture : ";tm("d");
cls="S9";os=OS[2];tm();os="";tm();
cls="b0";os="Assembly Company : ";tm("d");
cls="S9";os=OS[3];tm();//os="";tm();
cls="b0";os="Assembly Copyright: ";tm("d");
cls="S9";os=OS[4];tm();os="";tm();
cls="b0";os="Assembly Public Key: ";tm("dl");
cls="S9";os=OS[5];tm();os="";tm();
cls="b0";os="Assembly Key Token : ";tm("d");
cls="S9";os=OS[6];tm();os="";tm();
}
}
===============================================================================================

===============================================================================================
How to create an assembly:
==========================
You already know that an assembly can be of type "library" or "executable". The only difference
between an executable class and a library class is that the executable class contains an entry
point, which is at method main() Library classes don't contain that method and this is why they
cannot be executed, they can only contain methods which help an executable class. You create an
assembly by calling method sm("as") supplying it with the following parameters:
fls : Assembly file name. Extension can be "exe" or "dll".
LS[]: AssemblyName data: LS[0]=Name(Optional) LS[1]=Version LS[2]=Culture,
LS[3]=Company LS[4]=CopyrightInfo LS[5]=KeyFile
ks : Name of the class with entry point (for executable assemblies only)
KS[]: All classes which should be included into the assembly except the one in (ks)
JS[]: All resource files to be embedded into assembly.
js : Paths of external files which contain refrenced objects within assembly
seperated with commas.
kb : Window application flag. kb=true means windows executable (clickable) file.
ib : "Compile without additives" flag. ib=true: No main() or using directives are
to be added to any class within assembly.
In each executable assembly, you must specify which class is to be the executable class of the
assembly. You do so by assigning its name to (ks) That class will be compiled either as console
executable or window executable depending on the value you assign to (kb) All other classes
which you assign to KS[] will be compiled as library.
If you like your assembly to be compiled as library assembly, keep ks="" and assign all the
classes which make the assembly to KS[].
Notice that the assemblyName data which you assign to LS[] when you call method sm("as") is the
same as the assembly NameData which you receive into OS[] when you call method sm("agN") except
for the last two rows. LS[5] is the name of the key file where the public key or the keypair are
to be found while OS[5] is the public key and OS[6] is the key token.
How to include a class which was not made for PC#:
--------------------------------------------------
When you write classes which are to be compiled with PC# tools, you add only the using directives
which the tools will not automatically add for you. PC# reference contains a list of the using
directives which are automatically added. Additionally, if your class is executable, method
main() is also added to your code. When you set an assembly using method sm("as"), you expect
the same additions.
If you have written your own method main() into your class and using directives which you like to
keep, what to do?
For the using directives there is no serious problem since PC# method inspects what you have and
makes sure no duplicate directives are added, but for method main, it will be a problem since
you may end with two methods causing a compiler error.
To solve this problem, you add (ib=true) to your parameters which you supply to method sm("as")
This means "Add nothing to all my classes". You need to know here that this will apply to all the
classes which are assigned to both (ks) and KS[]. So if you make this selection, your
executable class should contain its own method main() and all classes must include every using
directive they need.
What to do with referenced objects:
-----------------------------------
You already know that when a class extends or instantiates another class, and you are using
PC# tools for compilation, you must specify the path of the library or executable file(s) which
contains the extended or instantiated class or classes. You do so in a commented line
immediately under the using directives and above the class or namespace declaration statement
like this:
//assembly a.exe, c:\\b.dll;
Notice that there must be no space between the "//" and the following letter "a". Files of all
referenced objects must be listed seperated with commas. You may have more than one line, but
all must start with //assembly and all must be above class or namespace declaration statement.
Classes (pcs) and (pasp) are extended by most of our classes, so why don't we list their files
like we do with other referenced object files? The answer is that all the tools you compile
with add the two files to your list internally.
With method sm("as") things are not the same. Here are the differences:
(1) Classes (pcs) and (pasp) files are not added unless you assign them to the list in (js)
(2) When class (x) references class (y) and both classes are going to be included into the
assembly, don't add either class to the list in (js). Add only the files of referenced
objects which are not going to be included into the assembly.
You may like to say here that you like to add class (pcs) or (pasp) to the assembly too,
so why do you need to add them to (js)? The answer is that you cannot add them to the
assembly since you don't have their ".cs" files. You can't include any class in (ks) or KS[]
unless its ".cs" file is available into your application directory.
(3) You need to know that the files listed in (js) must stay in same places after the assembly
has been created and that they must be accessable to the assembly. If the assembly will be
used into another computer, those files must also be there.
(4) Despite this problem, method sm("as") can help you with including class (pcs) and/or (pasp)
into your assembly and accessing them there. To make use of this add either "pcs.exe",
"pasp.dll" or both to the resource files list in JS[]. When method sm("as") finds them
there, it will add the software necessary to your executable class to extract the files and
supply them to the CLR whenever requested. You'll need to modify your class too. We'll see
an example on that later.
How to create a key pair file to sign assemblies with?
------------------------------------------------------
Method sm("akn") will generate a keypair and store the pair into file "KeyPair.pr". The public
key alone will be stored into file "PublicKey.pk". You need to rename those files and keep
them into a safe place. They should be used to sign all your assemblies.
IMPORTANT REMARK:
=================
Rename the files but Do NOT rename the extensions. The extensions tell method sm("cs") which
key you are signing with.
To fully sign an assembly, assign the name of the keypair file (extension "pr") to LS[5].
A hash code signature will be generated and also the Public Key will be stored into the assembly.
To delay-sign your assembly, assign the name of the public key file (extension "pk") to LS[5].
The public key will be stored into the assembly, but the hash signature will be null.
Later in this section we'll discuss the subject of delay signing in more details.
REMARKS:
--------
(1) All data in LS[] are optional.
(2) ks and KS[] contain class names only while js and JS[] contain full file path names.
(3) All resource files which you can include into an assembly and expect to use them while they
are embedded into the assembly must be for read only. You can't write into an embedded file.
However, you can extract a resource file and save it to disk then you can write into it.
(4) (js) contains full pathes of the external assemblies whose objects are refrenced. Classes
within the assembly may reference one another, but you don't include refrenced internal
classes into the list.
(5) You don't have to compile any of the classes in advance, the ".cs" files only must be
available, so all classes are compiled together to produce the assembly. However, we normally
compile the classes to make sure they run correctly before inserting them into the assembly.
(6) The (//assembly) statement which you follow with files of referenced objects is necessary
only when you run the classes outside the assembly. In the assembly, they are neglected.
The referenced object list in (js) is the one used for this purpose.
(7) Although you supply full path names of resource files into JS[]. The assembly reads the file
names only and use them to identify resources with. For this reason, if you try to add two
resources to the assembly which have the same file name but different paths, you'll receive
an error message. So make sure every resource file name in your application is unique
regardless to which subfolder it's located in.
(8) As you must know, arrays LS[], JS[], KS[] and all other general use arrays are initialized
with 100 rows each, all rows are assigned empty strings ("") to start with. They are fine to
work with as they are. However, you may change their capacities if you like by calling
dm("hg") from method init() You can also recreate them, but make sure no array which you
recreate and supply to any of our methods contains null rows.
Simulation of PC# tools:
========================
The tools we use to compile classes with, are examples of how to create the simplist assemblies.
We need to do the jobs of tools "pcp", "pwp" and "pcl" with one single class named "comp"
All parameters will be supplied to the class as command mode parameters. We like to make the
class do more than what the tools do by accepting the "No Additives" parameter.
-----------------------------------------------------------------------------------------------
public class comp : pcs {
string Class="";bool NoAdditives=false; // Class to be compiled, No Additive flag
public override void init() {
bli=0; // Default: blp=0 meaning "do nothing".
dm("pc"); // Get cmd parameter count
if (o>1) { // If there are parameters other than program name:
i=1;dm("p"); // Get 1st one which can be: ce=Console executable,
n="ce we lb ".IndexOf(os); // we=Windows executable, lb=Library. Make them map
bli=1+n/3; // to bli= 1, 2 or 3 respectively.
}
if (o>2) { // If more parameters are available:
i=2;dm("p"); // Get 2nd one which is name of the class to be
Class=os; // compiled. Assign it to (Class)
}
if (o>3) { // If more parameters are available:
i=3;dm("p"); // Get 3rd one which can only be "na" meaning
if (os=="na") NoAdditives=true; // No Additives. Set the flag accordingly.
}
base.init(); // Initialize parent class.
}
public override void run() {
cm("fe"); // Eliminate form.
if (blp==1) { // pcp
ks=Class;js="pcs.exe";fls=Class+".exe";ib=NoAdditives;sm("as");
}
if (blp==2) { // pwp
ks=Class;js="pcs.exe";fls=Class+".exe";ib=NoAdditives;kb=true;sm("as");
}
if (blp==3) { // pcl
KS[0]=Class;js="pcs.exe";fls=Class+".dll";ib=NoAdditives;sm("as");
}
}
}
-----------------------------------------------------------------------------------------------
Compile with "pcp comp" then:
To do the same job as (pcp a), type: comp ce a [ENTER]
To do the same job as (pwp a), type: comp we a [ENTER]
To do the same job as (pcl a), type: comp lb a [ENTER]
To do the same job as (pwp a), but with no additives: comp we a na [ENTER]
REMARK:
-------
Notice that in this class, we added "pcs.exe" to (js) This is because we know this class is
going to be used into your application directory where "pcs.exe" will always be available. If
we have intended to use "comp.exe" somewhere else. we should have added "pcs.exe" to JS[]
as explained before under the title "What to do with referenced objects".
How to retrieve resource files:
===============================
As mentioned above, if you include class (pcs) or class (pasp) into the assembly as resource
file, PC# software will be taking care of supplying it to the CLR on demand. How about other
resource files like images or read only text files? The executable class of the assembly must be
able to use them while they are inside the assembly. In some cases, you may like to retrieve a
resource to use it in a special manner or to save it to disk outside the assembly.
Method sm("agr") can retrieve a resource file from the assembly. It requires the name of the
assembly file which contains the resource assigned to (fls) If it was the assembly which
contains the calling class, supply (fls="") The method allows three options for output:
(1) If you supply (ks=""), it will return the resource file to you in the form of the present
Stream object (stp)
(2) If you supply (ks="@"). it will return to you the resource file in the form of bytes into
the byte array OY[].
(3) If you supply (ks="f"), it will save the resource to a file with the same name and path as
the original (relative to the folder where the assembly is running) This may take the
creation of new subfolders.
Additionally, in all cases you supply [js="Resource file name only (Not original full path)]
This is the name which the assembly identifies resource files with. You cannot store full paths
into the assembly. However, during assembly creation, method sm("as") solves this problem by
storing pathes of all resource files into a text file and storing the text file into the assembly
as an additional resource. This is where we get path information for resource files.
Examples:
---------
If the image file "pix.jpg" was to be embedded into the assembly you are creating, you should
have included "images\\pix.jpg" into JS[].
To receive the file data in the form of stream (stp) : js="pix.jpg";ks="";sm("agr");
To receive the file data in the form of byte array OY[] : js="pix.jpg";ks="@";sm("agr");
To save the resource to a disk file at original rel path: js="pix.jpg";ks="f";sm("agr");
How to develop the classes of an assembly:
==========================================
There are few difficulties in developing classes which are going to be inserted into assemblies.
Imagine if your class uses the graphics file "images\flower.jpg" to create and display a bitmap
object as follows:
-----------------------------------------------------------------------------------------------
public class a : pcs {
public override void run() {
j=k=300;cm("fs"); // Resize form to a smaller size.
fls="images\\flower.jpg"; // Original resource file path
gm("blf"); // Create a new bitmap object using the file.
gm("br"); // Render (bip)
}
}
-----------------------------------------------------------------------------------------------
Combining class (a) and the image it accesses into an assembly:
---------------------------------------------------------------
Class (c) will be used to make the assembly "MyAssembly.exe" which contains class (a) and the
resource file "images\\flower.jpg".
-----------------------------------------------------------------------------------------------
public class c : pcs {
public override void run() {
cm("fe");
ks="a";fls="MyAssembly.exe";js="pcs.exe";JS[0]="images\\flower.jpg";sm("as");
}
}
-----------------------------------------------------------------------------------------------
Compile class (c) with tool (pcp) and run it. The assembly file "MyAssembly.exe" should show up
into the directory. Try running it in a different folder (make sure to copy pcs.exe to that
folder too), you should get an exception.
When both the class and the resource have been outside the assembly, everything has been working
fine. After you created the assembly, the graphics file "flower.jpg" has become embedded into the
assembly. It can only be accessed as a stream. The assembly is now in a different location
and subfolder "images" no longer exists. So your code of class (a) should generate an exception.
How can we solve this problem?
You need to rewrite class (a) as:
-----------------------------------------------------------------------------------------------
public class a : pcs {
public override void init() {
asa="MyAssembly"; // asa=Name of the assembly without extension.
base.init();
}
public override void run() {
j=k=300;cm("fs"); // Resize form to a smaller size.
if (asb) { // If inside the assembly specified into init():
fls="";js="flower.jpg";ks="f";sm("agr"); // Copy resource to file at same location as
} // original relative to assembly's folder
fls="images\\flower.jpg"; // Now, whether class is running within assembly
// or not, file is available at same place.
gm("blf"); // Create a new bitmap object using (fls)
gm("br"); // and render (bip) in either case.
}
}
-----------------------------------------------------------------------------------------------
Class (pcs) has solved this problem for you by supplying you with the "In Assembly" flag (asb)
This flag is set when the class runs inside the assembly and is reset when it runs outside it.
This is done during initialization of class (pcs) It compares the current assembly name with
the name which you supply into method init() asigned to (asa) If they are found to be equal,
the flag is set. You use the flag to condition how to get the file in each of the two cases, so
your class runs fine during and after the development.
You can get the resource by calling method sm("agr") Supplying fls="" to this method tells it
that the assembly which is calling you is what we want to retrieve the resource from. As you
already know, the method gives 3 choices to obtain the resource and we choosed the third one.
So we supplied the method with (ks="f") and expected it to retrieve the resource and save it
into a file at same location as the original one relative to the assembly's folder.
Save class (a) code into file "a.cs". Rerun class (c) then run "MyAssembly.exe"
Since class (a) has been designed to produce the same output when it runs inside or outside its
assembly, you will see no difference between executing "MyAssembly.exe" and executing "a.exe".
Create a new directory and copy the two files "MyAssembly.exe" and "pcs.exe" to it. Run the
assembly there. It should work fine and you should see the created subfolder "images" which
contains file "flower.jpg" in the new directory. When you call method sm("agr") requesting to
copy an embedded resource to disk, the method guarantees that the resource file will have the
path relative to the folder where the assembly is which is identical to the original.
Our final job now is to include "pcs.exe" into the assembly in order to eliminate the need
of copying it to the new folder.
Including class (pcs) into the assembly:
========================================
This is the important one. You need to modify class (c) to make "pcs.exe" available as a resource
into JS[] so that its data could be retrieved and supplied to the CLR on demand. You also still
need to assign it to (js) since file "pcs.exe" of your application directory will be needed during
the process of making the assembly. It will not be needed therafter and the assembly will be able
to run anywhere as a single file which includes everything it needs internally.
==============================================================================================
Example 7: Create an assembly which contains an image and a class to display it. Class (pcs)
must be included into the assembly so that the assembly file becomes the only file necessary
for the operation.
==============================================================================================
public class c : pcs {
public override void run() {
cm("fe");
ks="a";fls="MyAssembly.exe";js=JS[0]="pcs.exe";JS[1]="images\\flower.jpg";sm("as");
}
}
----------------------------------------------------------------------------------------------
Next, you need to modify class (a) Class (a) cannot extend class (pcs) since if it does, it
will be referencing an object which could not be located. It's true that we'll be delivering
"pcs.exe" to the CLR, but this will happen later when method main() of class (a) runs. Method
main() which has been added to the class when the assembly was created tells the CLR how to
get class (pcs) in the form of binary data when it needs it.
So, the answer is to make our class which displays the image, a nested class of class (a) This
will allow the CLR to get the message before it discovers the extension of class (pcs) by the
nested class (N) The outer class (a) will be extending Form which is class (pcs)'s parent.
The next problem is that whenever the nested class draws something it will be drawing it on its
own form which we cannot see. We can only see the outer class's form. This can be solved
easily by drawing (bio) of the nested class on the form of the outer class. This process must be
done inside method OnPaint()
Method OnPaint() is called by thr CLR many times. So, we use the flag (rb) to make sure that
method run() of the nested class is executed only once.
----------------------------------------------------------------------------------------------
public class a:Form { // The outer class:
Graphics gra; // Graphical object ref
N n=new N(); // Inistantiate nested class,
bool rb; // Flag to insure that nested class runs once only.
protected override void OnPaint(PaintEventArgs e) {
if(!rb) { // If nested class has not run yet:
n.run(); // Run it and
rb=true; // set flag so it cannot not run again.
}
gra=e.Graphics; // Get graphical object of outer class's form.
Form frm=(Form)n; // Get nested class's Form.
this.Size=frm.Size; // Make the two forms equal in size.
gra.DrawImage(n.bio,0,0); // Draw default graphical device of inner class.
} // on outer class's form.
//------------------------------------- The nested class -----------------------------------
public class N : pcs {
public override void init() {
asa="MyAssembly"; // asa=Name of the assembly without extension.
base.init();
}
public override void run() {
j=k=300;cm("fs"); // Resize form to a smaller size.
if (asb) { // If inside the assembly:
fls="";js="flower.jpg";ks="f";sm("agr");
} // Copy resource to a file with same path as originl
fls="images\\flower.jpg"; // Either way, path to assign to (fls) is the same.
gm("blf"); // Create a new bitmap object using the file.
gm("br"); // Render (bip)
}
}
}
==============================================================================================
You may like to compile class (a) and run it to make sure that it runs fine before it's inserted
into the assembly. Now, run class (c) again to recreate the assembly "MyAssembly.exe". Copy the
generated assembly to a completely empty folder and run it there. It should run fine too.

===============================================================================================
You need to be aware of this:
=============================
Methods init() and setup() of the created instance of class (N) run automatically, when the
instance is created. Method run is executed within method OnPaint() of the outer class. Does
this accomplishes the same as if the inner class was the top class which runs by itself? The
answer is: Not exactly. Two items of the normal initialization procedures are not done, which
are:
(1) Making (bio) the default graphical output device.
(2) Creating a default pen and brush using solid black color.
You can easily solve this problem by adding these two lines at the start of mathod run():
gm("sdd"); // Make the graphical output device the default bitmap object.
cls=("S9");gm("sps"); // Create black pen and brush.
This is necessary only when the nested class performs drawing which was the case of the
previous example. However, we did not add the two lines since resizing the form has executed
gm("sdd") internally and we have had no need for a pen or a brush in that example.
Creating complex assemblies:
============================
We'll attempt this time to make an assembly which contains 4 classes with internal extensions
and instantiations. The assembly is made of 3 library classes A1, A2 and A3 each one of them
contains a method which receives a number and returns it back incremented.
Class A2 extends class A1. Its method a2() executes method a1() of class A1 before it increments
the number, so the number is incremented twice.
Class A3 instantiates class A1. Its method a3() also executes a1() before it increments the
number, so the number is also incremented twice by class A3.
Class (a) is an executable file. It extends class (pcs) and instantiates classes A2 and A3.
It receives a number from the user, applies method a2() and method a3() to the number in
sequence, so the number is incremented 4 times. It displays the result to the user.
-----------------------------------------------------------------------------------------------
public class A1 { // Class A1
public int a1(int x) { // Method a1() receives an int.
return (x+1); // and returns it incremented.
}
}
-----------------------------------------------------------------------------------------------
//assembly A1.dll; // A1's file must be listed here since it's extended.
public class A2:A1 { // Class A2 extends class A1
public int a2(int x) { // Method a2() receives a number,
x=a1(x); // increments it using a1()
return (x+1); // then increments it again.
}
}
-----------------------------------------------------------------------------------------------
//assembly A1.dll; // A1's file must be listed here since it's instantiated.
public class A3 { // Class A3
public int a3(int x) { // Method a3() receives a number,
A1 a1o=new A1(); // creates an instance of A1,
x=a1o.a1(x); // increments the number using a1()
return (x+1); // then increments it again.
}
}
-----------------------------------------------------------------------------------------------
//assembly A1.dll,A2.dll,A3.dll; // Refrenced objects list must include A1 additionally to A2,A3
// because A1 is refrenced in A2,A3 which are instantiated here
public class a : Form { // Class (a) extends (Form)
N n=new N(); // Inistantiate nested class,
bool rb; // Flag to insure that inner class runs once only.
protected override void OnPaint(PaintEventArgs e) {
Form fr=(Form)this;fr.Close(); // Close form.
if(!rb) { // If inner class has not run yet:
n.run(); // Run it and
rb=true; // set flag so it does not run again.
}
}
public class N : pcs { // Class (N) extends (pcs)
public override void run() {
cm("fe"); // Eliminate form.
A2 a2o=new A2(); // Instantiate A2
A3 a3o=new A3(); // and A3.
os="When finished, press [ENTER] with no data.";tm();
while (true) { // Start of endless loop
os="Enter any number:";tm("i");
if (os=="") break; // Get number from user, exit if no number entered.
om("ti");x=o; // Convert number to int, assign to (x)
x=a2o.a2(x); // apply method a2() to the number,
x=a3o.a3(x); // then method a3()
o=x;om("fi"); // Convert result to string
os="Result="+os;tm(); // and display it.
}
}
}
}
-----------------------------------------------------------------------------------------------
We have used the same design which was used in previous example. We made our class a nested
class and named it (N) The outer class (a) instantiates it and runs it only. Since the Console
is used for display, nothing else is necessary to be done. One good thing about the console is
that it's common to all. So whatever class (N) displays on the console is visible to us.
-----------------------------------------------------------------------------------------------
Save classes into files "A1.cs", "A2.cs", "A3.cs" and a.cs respectively. Compile classes as
follows:
pcl A1 [ENTER] pcl A2 [ENTER] pcl A3 [ENTER] pcp a [ENTER]
Run class (a) and confirm that it adds 4 to any number which you enter.
-----------------------------------------------------------------------------------------------
Now it's time to create an assembly which contains all 4 classes. One good thing about the
assembly is that you don't worry about internal referencing within the assembly. The referenced
objects list which you assign to (js) should include only refrenced external objects. Just like
we did in last example, File "pcs.exe" will be embedded as a resource. Additionally, adding it
to the refrenced object files list is necessary during compilation.
-----------------------------------------------------------------------------------------------
public class c : pcs { // Class used to create the assembly
public override void run() {
cm("fe"); // Eliminate form.
KS=new string[] {"A1","A2","A3"}; // Non executable Classes.
ks="a"; // Executable Class.
js=JS[0]="pcs.exe"; // pcs.exe is referenced object file and embedded resource.
fls="MyAssembly.exe";sm("as"); // Name of resulting assembly.
}
}
-----------------------------------------------------------------------------------------------
Compile class (c) and run it. Move the resulting assembly (MyAssembly.exe) to a new empty
folder and run it there. It should bring exactly the same results as class (a) did.
Here is what you see on the Console:
----------------------------------------------
When finished, press [ENTER] with no data.
Enter any number:0
Result=4
Enter any number:5
Result=9
Enter any number:10
Result=14
----------------------------------------------
Using Controls into an assembly:
================================
So far, we have seen how to create an assembly which can perform drawing or can read and write
text on the Console. The only kind left is an assembly which contains controls and can handle
their events. Example 2 of the "Using Controls" chapter is good to be tried as the executable
class of an assembly. The assembly should be portable and should be able to run alone, just like
the assemblies which we have created in the last two examples.
If you remember how we have been creating multiple forms, you know that class (pcs) normally
mounts all controls directly on the form unless the "Multiple-form" flag is on. When this flag
is on, it mounts the controls on panel (pno) then mounts (pno) on the form. This is exactly
what we need here since we can get the controls to show up on the form of the outer class by
mounting (pno) of the nested class on it. So let us try that.
Let us make the assembly "windows executable" this time. Also, create a key to sign the assembly
with. Fully sign the assembly and enter the following data into it:
Name: a
Version: 1.2.3.4
Company: AnyCompany
Copyright: Copyrighted product
==============================================================================================
Example 8: Make Example 2 of the "Using Controls" chapter the executable class of an assembly.
Include class (pcs) into the assembly, sign it and enter the information listed above into it.
==============================================================================================
The executable class (Class a):
-------------------------------
public class a:Form {
N n=new N(); // Inistantiate nested class,
bool rb; // Flag to insure that inner class runs once only.
protected override void OnPaint(PaintEventArgs e) {
if(!rb) { // If inner class has not run yet:
n.run(); // Run it and
rb=true; // set flag so it does not run again.
}
Form frm=(Form)n; // Get (n)'s Form.
this.Size=frm.Size; // Make the two forms equal in size.
this.Controls.Add(n.pno); // Mount panel (pno) of inner class with all its controls.
}
public class N : pcs {
public override void init() {
ib=true;dm("fm"); // Set the multi-form flag.
base.init();
}
public override void setup() {
j=400;k=300;cm("fs"); // Resize Form to (400,300) Pixels
cs="bt0";cis="Add";j=120;k=100;i=90;o=30;cls="r0y0";
fns="trbsui16";id=0;cm("i"); // bt0: location (120,100), dimensions (90,30)
cs="bt1";cis="Select";j=120;k=50;i=90;o=30;cls="r0g7";fns="trbi16";id=2;cm("i");
cs="bt2";cis="Which Item is Selected?";i=340;o=30;cls="b0r7";
fns="trb16";id=3;cm("i");
cs="lb0";cis="Test ComboBox >";j=-110;k=-90;i=120;o=30;os="e";cls="r0s7";
fns="tri11";id=-1;cm("i");
cs="tf0";cus="Banana";j=-60;k=100;i=225;o=30;cls="b0p7";fns="trb16";id=4;cm("i");
cs="tf1";cis="";j=-60;k=50;i=225;o=30;cls="S9b7";fns="tr16";id=5;cm("i");
cs="ch0";cus="Select a fruit";j=80;k=-90;i=190;o=40;cls="b0m6";fns="trb16";
CIS=new string[] {"Apples","Oranges","Peaches","Pears"};
id=6;cm("i");
}
public override void update() {
if ("bt0".Equals(cs)) { // If bt0 clicked
cs="tf0";cm("gu"); // Get tf0 update (its text) into (cus)
CIS[0]=cus;cs="ch0";cm("sL"); // Add it to ch0's list.
}
if ("bt1".Equals(cs)) { // if bt1 clicked
cs="tf1";cm("gu"); // Get tf1's update value (its text) into (cus)
string text=cus; // Save (cus) temporarely
cs="ch0";cm("O"); // Make (chp) refers to ch0's object.
int index = chp.FindString(text); // Search it for tf1's text match
cus="";cui=index;cs="ch0";cm("su");// update ch0 (make cui its selected index)
}
if ("bt2".Equals(cs)) { // if bt2 clicked
cs="ch0";cm("gu");string text=cus; // Get ch0 update (its selected text)
os="Selected Item's Text: " + text + "\n" + "Index: " + cui;cm("d");
//Display results in a dialog
}
}
}
}
-----------------------------------------------------------------------------------------------
The Assembly Creator class (Class c):
-------------------------------------
public class c : pcs {
public override void run() {
cm("fe");
sm("akn"); // Create new keypair (into file "KeyPair.pr")
// Setting Assembly Name components.
LS[0]="a";LS[1]="1.2.3.4";LS[2]="";LS[3]="AnyCompany";
LS[4]="Copyrighted product";LS[5]="KeyPair.pr";
// Setting other parameters. Notice (kb=true) which means make it a windows-clickable file.
ks="a";fls="MyAssembly.exe";js=JS[0]="pcs.exe";kb=true;sm("as");
}
}
-----------------------------------------------------------------------------------------------
Compile class (c) and run it. Move the resulting assembly (MyAssembly.exe) to a new empty
folder and run it there to make sure it operates properly.
Modify example 6 so that it displays all name information of assembly "MyAssembly.exe".
Recompile example 6 and run it to make sure that all name data have been stored properly into
the assembly.
==============================================================================================

===============================================================================================
Delay Signing of an assembly:
=============================
When several developers are sharing the development of a project, it could be safer and more
convenient to sign your assembly temporarely with the public key, then fully sign it when the
project has been completed.
The temporarely signed assembly will not run unless you instruct the ".NET" runtime to stop
identity verification of the assembly with method sm("avn") "vn" is for "Verification: No".
After the assembly is signed, you should turn the verification process on with sm("avy")
Example:
--------
Modify class (c) of example 8 by changing the name of the file to sign with from "KeyPair.pr"
to "PublicKey.pk". Recompile class (c) and run it to reproduce the assembly "MyAssembly.exe".
Try to run the assembly, you should get this error message:
-------------------------------------------------------------------
MyAssembly.exe has encountered a problem and needs to close. We are
sorry for the inconvenience.
-------------------------------------------------------------------
Now, instruct the .NET runtime to stop verifying assembly identities when signatures are
unavailable by adding one more line at the end of class (c)'s method run(), as follows:
----------------------------------------------------------------------------------------------
public class c : pcs {
public override void run() {
cm("fe");
sm("akn");
LS[0]="a";LS[1]="1.2.3.4";LS[2]="";LS[3]="AnyCompany";
LS[4]="Copy righted product";LS[5]="PublicKey.pk"; // Sign with public key.
ks="a";fls="MyAssembly.exe";js=JS[0]="pcs.exe";kb=true;sm("as");
fls="MyAssembly.exe";sm("avn"); // Do not verify identities.
}
}
----------------------------------------------------------------------------------------------
Recompile class (c) again and run it, then try running the new assembly. It should run fine
with no errors this time.
To fully sign the assembly, you should redo its creation using your keypair file for your
signature. Then you should execute sm("avy") as follows:
----------------------------------------------------------------------------------------------
public class c : pcs {
public override void run() {
cm("fe");
sm("akn");
LS[0]="a";LS[1]="1.2.3.4";LS[2]="";LS[3]="AnyCompany";
LS[4]="Copy righted product";LS[5]="KeyPair.pr"; // Fully Sign using keypair.
ks="a";fls="MyAssembly.exe";js=JS[0]="pcs.exe";kb=true;sm("as");
fls="MyAssembly.exe";sm("avy"); // Reinstate identity verification.
}
}
----------------------------------------------------------------------------------------------
Storing your assembly into the Global Assembly Cache (GAC):
===========================================================
An assembly which is stored into the GAC can be refrenced from any where within your computer.
The ".NET" assemblies which you reference in your classes are stored there. You cannot store an
assembly into the GAC unless it's signed.
You may look at storing your assembly there as a good idea or you may look at it as an added
complexity which you have no need for. The decision is yours, however we have the methods which
can make this job simpler. If your assembly name was "MyAssembly.exe" here is what you can do:
fls="MyAssembly.exe";sm("aGi"); // Installs your assembly into the GAC.
sm("aGl"); // Returns a list of all assemblies currently
// in the GAC assigned to (os)
To Uninstall your assembly use this command mode tool:
gacutil /u AssemblyName AssemblyName is the name of your assembly without extension.
----------------------------------------------------------------------------------------------
Including "pcs.exe" into an assembly with a class which has not been made for PC#:
==================================================================================
You already know that if you include method main() into your executable class and all the
"using" directives necessary to each class into your project, you can create an assembly using
method sm("as") if you supply it with (ib=true;)
Now, if your classes require class (pcs) or class (pasp) to be included into the assembly,
what can be done? Setting (ib=true) will disallow adding the required code to supply those
classes binary code to the CLR whenever requested. So, you need to add the necessary code to
your executable class by yourself.
Let us have an example. We'll assume that your executable class contains its own method main()
and user directives, additionally it contains an inner class which extends class (pcs). You
like to create an assembly which includes your class together with class (pcs), so it can run
anywhere as a single file.
================================================================================================
Example 9: The best to use for this demonstration is example 16 of the "Graphics" section
that uses the TextScreen for drawing. The TextScreen is made of variety of controls which
require internal event handling. You'll add to your class method LoadAssembly() which will
supply class (pcs) on demand and add to method main() the code which tells the CLR where to find
help to resolve (pcs)
================================================================================================
Assembly's Executable class:
----------------------------
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
public class a:Form { // Class a extends Form
N n=new N(); // Inistantiate nested class,
bool rb; // Flag to insure that nested class runs once only.
[System.STAThreadAttribute()]
public static void Main(String[] args) {
AppDomain.CurrentDomain.AssemblyResolve += LoadAssembly;
a xx=new a();
Application.Run(xx);
}
protected override void OnPaint(PaintEventArgs e) {
if(!rb) { // If nested class has not run yet:
n.run(); // Run it and
rb=true; // set flag so it does not run again.
}
Form frm=(Form)n; // Get (n)'s Form.
this.Size=frm.Size; // Make the two forms equal in size.
this.Controls.Add(n.pno); // Mount panel (pno) of inner class with all its controls.
this.Menu=n.mma; // Install TextScreen main menue on this form.
}
//------------------------------------ Nested Class ----------------------------------------
public class N : pcs {
public override void init() {
ib=true;dm("fm"); // Set the multi-form flag.
tia=toa="t"; // Select "text screen" for both text input & output
base.init(); // Must be the last in init()
}
public override void run() {
gm("sdd"); // Make (bio) the graphical output device.
cls="r0";fns="trb18";os="Drawing on the Text Screen";tm();os="";tm();
// Display title
cls="b0";fns="trb12";os="This text line comes before the drawing.";tm();
// Display first line on Text Screen
tm("vg"); // Give visibility to Drawing
j=k=200;ib=true;cm("fs"); // Resize form to fit object
cls="s7";gm("ec"); // Color background to match Text Screen.
//--------------------- Drawing the object (No change in code) --------------------
lf=of=170;gm("ce"); // Create the circular gold plate
cls="o5y5";gm("spl"); // Prepare linear gradient brush for it
gm("grf"); // then render-fill the gold plate.
lf=6;of=50;gm("c="); // Create hexagon shape object at center.
cls="r0";ks="r";gm("grs"); // Draw the object using sp effects-refl at
jf=45;cls="b0";ks="r";gm("grs"); // center in red, then repeat 6 times using
jf=-45;cls="b0";ks="r";gm("grs"); // different colors and different locations
jf=22;kf=40;cls="g0";ks="r";gm("grs");
jf=-22;kf=40;cls="m0";ks="r";gm("grs");
jf=22;kf=-40;cls="m0";ks="r";gm("grs");
jf=-22;kf=-40;cls="g0";ks="r";gm("grs");
lf=of=25;gm("ce"); // Create a circle at center (pearl)
for (int x=0;x<20;x++) { // Draw it 20 times using sp effects-reflection
jf=80;kf=18*x;kb=true; // at locations around the plate. Polar coord's
cls="p0";ks="r";gm("grs"); // are used for specifying locations
}
//----------------------- Displaying (bio) on Text Screen -------------------------
imp=bio;sm("csi"); // Set (bio) into Clipboard
cm("fsd"); // Return form to default size.
tm("vt"); // Return Visibility to Text Screen
tfa.Hide();lba.Hide(); // ** See Remark.
os=" ";tm("d"); // Move cursor to wanted display position
tm("ep"); // Text Screen edit-paste
os="";tm(); // Move to next line
cls="b0";os="And this one comes after the drawing.";tm();
} // Display second line on Text Screen.
}
//----------------------------------- Event Handler ---------------------------------------
static Assembly LoadAssembly(object sender,ResolveEventArgs ar) {
string os=new AssemblyName(ar.Name).Name; // Get name of requested assembly
if (os.IndexOf("pcs")>-1) { // If was found to be "pcs":
Assembly asp=Assembly.GetExecutingAssembly(); // asp=running assembly
Stream stp=asp.GetManifestResourceStream("pcs.exe");// Create Stream to embedded pcs.exe
long ol=stp.Length; // ol=Length of pcs.exe in bytes.
byte[] OY=new byte[ol]; // Create byte array of equal size.
stp.Read(OY,0,(int)ol); // Read pcs.exe into byte array.
stp.Close(); // Close the stream.
return Assembly.Load(OY); // Load OY[] into an Assembly and
} // return it.
else return null; // If different assembly was requested
} // return null.
}
----------------------------------------------------------------------------------------------
REMARK:
-------
Notice the line which we have added after the Text screen was made visible:
tfa.Hide();lba.Hide(); // Hide the bottom label and text field.
When the TextScreen is set, the bottom label and text field are installed into a panel which
is docked to the bottom of the Form. So, whenever the user resizes the page, those two components
always move to the new form buttom and stay there. This works fine when the class which extends
(pcs) and creates the TextScreen is the top class.
Now, things are different. The top class has received the TextScreen once only when it was at
its small size. When the screen is enlarged, the TextScreen remains the same keeping itself
at the top left corner of the screen. The only problem with this is that the text field and the
label control will be hanging in at the middle of the screen. Those two controls are unnecessary
for this example, so the best answer is to make them invisible. Fortunately, all text screen
control object ref var's are public and you know their names, so you can do this job easily. You
should hide either (pna) or both (lba) and (tfa)
How about if your application requires user input, so you need the text field and label to be
available? In this case, one option is to make the TextScreen at full screen size to start with,
so resizing it will not change anything. To do so, change the size setup in method init() to:
j=k=0;dm("s"); // Set form size at maximum size (Which is full screen size)
A better solution would be to override the protected method OnResize() and fix it there.
----------------------------------------------------------------------------------------------
Assembly creator class:
-----------------------
public class c : pcs {
public override void run() {
cm("fe"); // Eliminate Form
ib=true; // No additions requested.
fls="MyAssembly.exe"; // Assembly file name.
ks="a"; // Executable class name.
js=JS[0]="pcs.exe"; // Class (pcs) must be on both lists (Referenced obj's & Res. files)
kb=true;sm("as"); // Make it a windows application.
}
}
----------------------------------------------------------------------------------------------
Save class (a) into file (a.cs) Do not compile it.
Save class (c) into file (c.cs) and compile it with: pcp c [ENTER]
Run (c), The new assembly file "My assembly.exe" should show up into current folder. Move
it to a new empty folder and try it. It should produce the output shown below.
==============================================================================================