Getting a file's ContentType

  • Thread starter Thread starter Nathan Sokalski
  • Start date Start date
N

Nathan Sokalski

I would like to be able to get the ContentType of a file programmatically
(for example, I want *.txt files to return "text/plain"). I could not find a
way to do this using VB.NET's classes, but I am also somewhat new to using
the System.IO namespace for anything other than text files and the pure
basics, so I could very well have missed it. Any help would be appreciated.
Thanks.
 
The following uses Internet Explorer to find the MIME type:

[DllImport("urlmon.dll", CharSet = CharSet.Auto)]
static extern int FindMimeFromData(IntPtr pBC, IntPtr pwzUrl, byte[]
pBuffer, int cbSize,
IntPtr pwzMimeProposed, int dwMimeFlags, out IntPtr ppwzMimeOut, int
dwReserved);

/// <summary type="System.String">
/// Figures out the Content Type from a File Name
/// </summary>
/// <param name="FileName">File Name to detect</param>
/// <returns>Content Type as string</returns>
/// <remarks>The Windows API has the functionality to detect the file type
of
/// any file type registered with the Operating System. This method makes an
API
/// call to get the MIME type of the file from the
<var>FileName</var></remarks>
public string MimeTypeFromFileName(string FileName)
{
byte[] dataBytes = Encoding.ASCII.GetBytes(FileName);
if (dataBytes == null)
throw new ArgumentNullException("dataBytes");
string mimeRet = String.Empty;
IntPtr suggestPtr = IntPtr.Zero, filePtr = IntPtr.Zero, outPtr =
IntPtr.Zero;
int ret = FindMimeFromData(IntPtr.Zero, IntPtr.Zero, dataBytes,
dataBytes.Length, suggestPtr, 0, out outPtr, 0);
if (ret == 0 && outPtr != IntPtr.Zero)
{
return Marshal.PtrToStringUni(outPtr);
}
return mimeRet;
}

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Numbskull

Hard work is a medication for which
there is no placebo.
 
Egbert said:
The trick mentioned works, but not always.
The safest way is to use this.
http://technolog.nl/eprogrammer/archive/2005/12/12/415.aspx

Just one problem with that technique - it's undefined whether it actually
works. FindMimeFromData returns memory allocated from the heap with new[],
so there's no way to correctly free that memory unless you can be sure
you're delete[]-ing it back into the same heap.

Despite being exported from the DLL, this function really wasn't written
correctly to be used outside a known environment.

-cd
 
Carl Daniel said:
Egbert said:
The trick mentioned works, but not always.
The safest way is to use this.
http://technolog.nl/eprogrammer/archive/2005/12/12/415.aspx

Just one problem with that technique - it's undefined whether it actually
works. FindMimeFromData returns memory allocated from the heap with
new[],

It uses allocated memory, in the buffer and it uses CoTaskMemAlloc, for the
mimetype string. If that were a new operator, well, I'm sure that's
compatible since the C++ new keyword, after all, just uses *the same* memory
allocator (ie HeapAlloc).
so there's no way to correctly free that memory unless you can be sure
you're delete[]-ing it back into the same heap.


That's why this declaration exists.
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1,
SizeParamIndex = 3)] byte[] pBuffer,
int cbSize,



if it were wrong, then MS would not support it. And besides, it never failed
in my runtime.

Despite being exported from the DLL, this function really wasn't written
correctly to be used outside a known environment.

I agree, but you might agree with me, that the whole .NET framework,
**depends** on unmanaged code, from kernel, ADSI, WMI etc, and originally,
these were never made with .NET in mind. It's the other way around, .NET was
made to interop with them. So I won't hesitate to use this code, unless you
have a proven example, that shows that it will go wrong.

Maybe a CoTaskMemAlloc would be better instead of a byte[] array, but that
won't interop with FileRead...


Here you have the generated ASM listing.
00000107 FF 75 B8 push dword ptr [ebp-48h]
0000010a 56 push esi
0000010b 6A 00 push 0
0000010d 6A 00 push 0
0000010f 8D 45 B4 lea eax,[ebp-4Ch]
00000112 50 push eax
00000113 6A 00 push 0
00000115 8B D7 mov edx,edi
00000117 33 C9 xor ecx,ecx
00000119 E8 1A 2C 86 FF call FF862D38 <-- call FindMimeFromData

and here is the IL

IL_0052: ldsfld native int [mscorlib]System.IntPtr::Zero
IL_0057: ldarg.0
IL_0058: ldloc.2
IL_0059: ldloc.0
IL_005a: ldnull
IL_005b: ldc.i4.0
IL_005c: ldloca.s V_3
IL_005e: ldc.i4.0
IL_005f: call int32 TimeStampTester.Class1::FindMimeFromData(native
int, string,uint8[], int32, string, int32, string&, int32)

As you can see, the compiler does not -worry- about packing the byte array
into something else! It just assumes, that during the Pinvoke, the memory
won't move. Now this is interesting. If I have the wrong approach, then MS
introduced a dangerous bug, by allowing us to program like this.
 
Kevin Spencer said:
The following uses Internet Explorer to find the MIME type:

[DllImport("urlmon.dll", CharSet = CharSet.Auto)]
static extern int FindMimeFromData(IntPtr pBC, IntPtr pwzUrl, byte[]
pBuffer, int cbSize,
IntPtr pwzMimeProposed, int dwMimeFlags, out IntPtr ppwzMimeOut, int
dwReserved);

/// <summary type="System.String">
/// Figures out the Content Type from a File Name
/// </summary>
/// <param name="FileName">File Name to detect</param>
/// <returns>Content Type as string</returns>
/// <remarks>The Windows API has the functionality to detect the file type
of
/// any file type registered with the Operating System. This method makes
an API
/// call to get the MIME type of the file from the
<var>FileName</var></remarks>
public string MimeTypeFromFileName(string FileName)
{
byte[] dataBytes = Encoding.ASCII.GetBytes(FileName);
if (dataBytes == null)
throw new ArgumentNullException("dataBytes");
string mimeRet = String.Empty;
IntPtr suggestPtr = IntPtr.Zero, filePtr = IntPtr.Zero, outPtr =
IntPtr.Zero;
int ret = FindMimeFromData(IntPtr.Zero, IntPtr.Zero, dataBytes,
dataBytes.Length, suggestPtr, 0, out outPtr, 0);


Hi Kevin,
outPtr leaks memory in your sample. You should free it using
CoTaskMemFree...

Cheers
 
Back
Top