What Great .NET Developers Ought To Know – Part 1

I found the page What Great .NET Developers Ought To Know already a while ago (in Summer 2017) and thought about going thoroughly through the list at some time… The time has come and I got through the items one by one. Due to the high number of them I split them further into parts. I have also decided to leave out some of the questions that I thought were not relevant anymore. There have been some, though, that I still went through and left my thoughts about.

Ready? Let’s begin!

Everyone who writes code

== Describe the difference between a Thread and a Process ==

A thread is a single unit of code execution, allocated processor time by the Operating System. It is run within a shared memory, which means there can be multiple threads sharing the same memory and resources. Each thread has its own set of processor registers and a stack.

A process consists of at least one thread. The process is given its own memory space. Even if the same executable is run, a separate process for it it created.

A process is a heavy entity and communication between processes is more complicated than between threads due to separate memory spaces. The communication between threads, on the other hand, is easy as they share the memory. As a consequence it is also easier for a thread to affect other threads. The same is not true in case of processes. If a process misbehaves, it can be closed without affecting other processes.

== What is a Windows Service and how does its lifecycle differ from a “standard” EXE? ==

A typical Windows Service is run within a generic host process called svchost.exe. The Windows Service is loaded from a dynamically linked library (DLL) file. It has a Start and Stop, does not have a GUI and is controlled by the service host. The conditions when the service is started and stopped is configured within Services (services.msc). Its lifecycle differs from a standard EXE in that the latter is usually under a direct control of a user. The user can start and stop the standard EXE from a command line or through a link. A service, as the name suggests usually provides a service within a system for other applications and is not intended to be directly controlled by the user.

== What is the maximum amount of memory any single process on Windows can address? Is this different than the maximum virtual memory for the system? How would this affect a system design? ==

The maximum amount of memory a process on Windows can address depends on whether the OS is 32-bit or 64-bit. For 32-bit the overall addressable memory space is 2^32 = 4GB. For 64-bit the memory space is 2^64 bytes. If the actual RAM memory is less than that, the data from the memory space is dumped into the hard-drive.

Coming back to the question. A single process can run either in user-mode (any typical Windows service or standard EXE) or kernel-mode (e.g. a system driver). In user-mode in 32-bit system, the process is limited to the first 2GB of memory space (Virtual Memory). The address space above 2GB to 4GB is reserved for kernel. The limitation is due to security reasons. A kernel-mode process can access both user-space and kernel-space.

In 64-bit OS, the user-mode memory space for a process is up to 8TB (tera-byte) (source).

How would the above affect the system design? As the process in user-mode does not have permission access addresses over the upper-limits, the OS can safely run without the risks of a buggy or malicious software affecting it. If there was no such a limit then each app would possibly have to be explicitly granted rights – during installation – to access certain kernel-mode address ranges.

== What is the difference between an EXE and a DLL? ==

An EXE file contains a computer program that is loaded into its own process memory space.

A DLL is a Dynamically Linked Library that is loaded into an existing process memory space. Therefore a DLL can be loaded into the memory space of an EXE process.

An EXE is a stand-alone application, while a DLL is a library and does not need to be a stand-alone application, although technically it can also be such one. It would only have to be loaded somehow into an existing process memory. Such case is with .NET Core applications that are distributed as DLLs and then loaded with dotnet command line tool.

Both an EXE and a DLL have got an Entry point invoked by the OS. The entry points differ in their purpose, but they always must be provided (source).

An EXE, being a stand-alone application cannot be loaded into other processes, while a DLL is intended to be loaded into one or multiple processes.

== What is strong-typing versus weak-typing? Which is preferred? Why? ==

Strong-typing allows for verification of types at compile-time greatly reducing risk of run-time type mismatches and errors. It requires the developer to explicitly declare the variable types upfront.

Weak-typing allows for writing code without explicit type declaration and and instead relying on implicit type casts whenever necessary. With weak-typing we achieve very concise and flexible code.

The preferred is strong-typing as it provides a more reliable and predictable code. The predictability of the code comes from the fact that it only accepts types it knows how to handle. In a weakly-typed language calling some piece of code could succeed as a result of a cast and still generate correct results in some cases, while in other cases the cast would lead to errors.

== Corillian’s product is a “Component Container.” Name at least 3 component containers that ship now with the Windows Server Family. ==

Personally I find this question quite odd. Why would “anyone who writes code” be supposed to be aware of some Corillian’s product in the first place?! Anyway, Google to the rescue.

So, it turns out that the Corillian’s product was supposed to give a big hint about the technology behind “Component Container” at the time the author formed this question. The component container concept belongs to Windows Forms and Web Forms – the old GUI technologies. Personally I have used only the Windows Forms with the MVPVM pattern.

If one wants to implement an object that can be placed on the design surface (in the Visual Studio Designer) then they must implement the System.ComponentModel.IComponent. If we want the object to have some UI capabilities then it has to be derived from  System.Windows.Forms.Control or System.Web.UI.Control.

When such object is placed onto the design surface it gets added to a container. We can implement our own container by implementing System.ComponentModel.IContainer. The container and its components would communicate with one another using sites (classes implementing System.ComponentModel.ISite).

Each of the components could also retrieve services from the containers by using the site’s interface IServiceProvider.GetService.

Examples of at least 3 containers:

  • System.Windows.Forms.Form
  • System.Web.UI.Page
  • System.ComponentModel.Container

== What is a PID? How is it useful when troubleshooting a system? ==

A PID is a Process Identifier in the form of an integer number. It uniquely identifies a currently running process within the OS. When troubleshooting a system, we can list all the running processes and then examine for example the amount of memory and CPU they use. To further investigate issues we can use PIDs to uniquely specify which processes we want to take a closer look at. If we want to terminate a process we can easily do so by calling:

taskkill /PID <the process identifier> /F

== How many processes can listen on a single TCP/IP port? ==

Only one process can open and then listen to a single port on a particular address. How come applications such as Fiddler can also listen on already opened ports? That is because they register themselves as proxies, therefore, what they do is intercept and pass the data coming in and out.

== What is the GAC? What problem does it solve? ==

GAC is the Global Assembly Cache for .NET DLL libraries. It addresses the problem of having multiple DLLs with different versions. If an application requires a particular assembly (e.g. a DLL) to be loaded into its memory space, it has to provide the full version of the assembly in the form of a strong name, so that the assembly can be fetched from the GAC. An example of a strong name:

mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=AMD64

To view what assemblies the GAC currently contains, you can go to Start, type “dev” and pick (assuming you have VS 2017 installed) “Developer Command Prompt for VS 2017”. Enter:

gacutil -l

and see the list of assemblies.

Leave a Reply

Your email address will not be published.