Personal C Sharp                                                         By  famsoft.org
HomeHow To StartExamples-DesktopExamples-WebPC# MethodsReference-DesktopReference-Web


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. ==============================================================================================