Basically, I believe that if you have something like a .dmg file the OS opens it with a certain program which takes the code behind it and does things to it. So why couldn't you then take the program that runs the .dmg file on Mac and put it on window to run the program? I know I'm probably missing some key component here that makes me wrong but I have no idea what; I'm a bit of a noob to so take it easy on me.
Think of the
.dmg file as a book of short stories.
Windows and Mac OS X have different formats of books, so the gesture to open one is a bit different (think of pages turned from left to right or right to left). Less metaphorically, a
.dmg file packs a bunch of files, and the format in which these files are packed isn't one that Windows supports natively. However it would be easy to write a program to unpack
.dmg files on Windows, and I'm sure someone has done so.
Ok, now you have the book open and can see the pages. That doesn't necessarily mean you can read it, though. Let's say you speak English and only English… but the book is in Turkish. You aren't going to be able to understand a word of it.
A computer program contains a bunch of elementary instructions. The instruction set is like the script that the book is written in. The instruction set is determined by the type of processor that powers the computer. Modern Macs and Windows PCs have the same type of processor (one of the two x86 families, either the 32-bit one or the 64-bit one). So that step is fine — you can make out the individual letters. However, that's not enough!
Some instructions rely only on the processor: instructions like “read the value at this memory location” or “add these two numbers” or “jump to this location in the program”. However, every time the program wants to interact with something else (access a disk file, read user input, display something, communicate over the network, etc.), the program makes a call to the operating system. And two completely different operating systems, such as Windows and Mac OS X, have completely different APIs — they offer completely different system calls. (Note that the metaphor breaks down somewhat here: the problem isn't that the arrangement of instructions has to be different, but that a few very important instructions have radically different effects.)
There's even another layer of differences: Windows and OSX even have different executable file formats (PE vs Mach-O). The executable format determines how code and data are arranged in the file and how things like dependencies on libraries are represented. Making Windows understand OSX's file format would require some work, but making Windows understand OSX's system calls would be a Herculean task.
You can't read the book because it's in Turkish. You could learn Turkish, but it would take years of effort. Similarly, making Windows able to run OSX programs would take a huge amount of programming effort.
In fact, it's simpler to simulate a processor with its peripherals, by implementing all of its instructions, than it is to simulate an operating system by implementing all of its system calls. This is called a virtual machine. You can run an operating system on an emulated machine that is in fact provided by a program running under another operating system. For example, you can run OSX on an x86 CPU emulated by Vmware which is running as a Windows application. (In the specific case of OSX, this is difficult, but not for technical reasons, rather because Apple makes it difficult for commercial reasons.)
Long story short - they are two different Operating Systems (OSes).
You probably think of an OS as the way a computer looks (the GUI) and what applications it comes with but an OS is a lot more than that. When a programmer wants to do something like display a single letter on the screen in their application it can actually be pretty complicated. For example when a PC is in text mode (what happens right before you seen the Windows loading screen) a program run at that time would have to write a letter to memory address 0xB8000. Why? Because video cards use memory based IO to allow programs to write to the screen. Not only is this really technical for you but it requires more hardware knowledge than most programmers have. Not only that but to make the same program do the same thing on a different type of computer where the video cards worked differently the programmer would have to know all about how the hardware worked on that system.
To make it so programmers could create applications without knowing a lot about the hardware one of the things that was created was an Operating System. Instead of trying to talk to the hardware directly a programmer will now ask the OS, "Hey OS can you display this text on the screen for me?". This is what is called a "system call". Part of the problem is that each OS and a completely different set of system calls which keeps a programmer from having to know about the hardware but makes them still have to learn how to ask each OS to do things.
So they tried to make a standard set of helper routines (libraries) that would work in all OSes. Think of it as a translator, a program asks the helper routine to print a formatted string (printf) which in turn actually converts that to the OS specific routine (probably called something like sprintf). This portable layer is called POSIX which you can read more about at https://en.wikipedia.org/wiki/POSIX. But as computers grew more powerful (for example the added the ability to display graphics where before they were only text based) the Operating Systems added more libraries for the applications to call which were not covered in the POSIX standard so developers that didn't want to reinvent the wheel by recreating all the features in an OS just ask the OS to do things which makes their application only work on a particular OS.
There is a little bit more to it than that (different file formats for executables, etc) but the libraries that an OS provides to an application and the underlying CPU types is really what make it difficult to copy applications over.
Sometimes you have the same OS (or close enough - the libraries are the same) but you have completely different CPUs. This can also cause problems. For example right now you can go get a Windows RT tablet. The "RT" tablet can not run normal Windows programs, even those that work on other Windows tablets. This is because they use a different CPU architecture. A CPU is the brain of the computer that does the majority of the work. The primary job of a programmer is to create instructions for a CPU. But it turns out this is really really hard. To make it easier they created something called a compiler. What a compiler does is it allows a programmer to write CPU instructions in something that almost looks like english (a high-level programming language) and it converts the almost-english text into the really hard to understand stuff that the CPU actually needs (opcodes) in order to know what to do. Basically the compiler is another type of translator that keeps programmers from having to know a lot of detail about the hardware (CPU in this case).
The problem here is that the output of the compiler is specific to a family of CPUs. Compiler output meant for Intel CPUs (Most PCs) won't work on ARM CPUs (Most tablets and phones).
All these translation layers help programmers worry less about the low-level details and focus more on the high-level logic or behavior of an application. As more translation layers and helper libraries (off the shelf functionality that doesn't need to be recreated) have been created they have enabled programmers to create more feature-full applications. The problem is since the applications they create have so many dependencies and are translated into CPU and OS specific stuff their applications become none-portable. Sometimes a developer takes the time to make sure their application will work on multiple types of CPUs and OSes, this is called porting.