Niall's answer is correct, but to me it felt like a little bit more information would help your understanding of the problem better.
3DES is alternately referred to in some specifications as DES-EDE, which is DES-Encrypt/Decrypt/Encrypt.
var x = Encrypt(key1, input);
x = Decrypt(key2, x);
x = Encrypt(key3, x);
return x;
So 3DES always requires 3 keys, each of which are have 56 key bits stretched into 64 bits (8 bytes) because every 7 bits gets a 1 bit parity check. This is frequently expressed as one 192-bit value (24 bytes) value, or an intermediate 128-bit value (16 bytes).
- If the 3DES key is 64 bits (which has a key strength of 56 bits, and many implementations will reject)
- k1 = key
- k2 = key
- k3 = key
- If the 3DES key is 128 bits (which has a key strength of 112 bits)
- k1 = key[0..7]
- k2 = key[8..15]
- k3 = k1
- If the 3DES key is 192 bits (which has a key strength of 168 bits)
- k1 = key[0..7]
- k2 = key[8..15]
- k3 = key[16..23]
So if we have a 64-bit 3DES key we encrypt with the key, then decrypt with the key (returning the original data) and then encrypt with the key again. That makes "one key" 3DES equivalent to (1)DES.
Note that the DES-equivalent behavior can happen for two-key 3DES if k2 is coincidentally k1, (or 3-key if k1=k2=k3) so using 3DES isn't always an upgrade over using DES.
As for why the implementations differ: In C# arrays are length tagged, so passing one array the recipient can check if you are passing 8, 16, or 24 bytes. In C arrays aren't length tagged, so the API needs to either ask you how long your data is (which is what Windows CAPI and CNG do) or just take three different key pointers, and make you do the fragment cloning for 1-key and 2-key keys.