"InteropServices.SEHException: External component has thrown an exception" - Solved!
I finally solved the problems with enabling XP Visual Styles (Themes) which I described here. My initial attempt worked on some computers but not others; apparently there is some mystic connection between the themed activation context, the color of my underwear and the current conversion rate from yen to zloty. Anyway, a huge thanks to Nat (whoever you are) for the helpful tips regarding various aspects of manifest creation and automation.
The solution? Skip the default C# implementation (which is buggy) and implement your own, as detailed here:
or just copy the following code into a class:
using System.Runtime.InteropServices;
using System;
using System.Security;
using System.Security.Permissions;
using System.Collections;
using System.IO;
using System.Text;
using System.Windows.Forms;
[ SuppressUnmanagedCodeSecurity ]
internal class EnableThemingInScope : IDisposable
{
// Private data
private uint cookie;
private static ACTCTX enableThemingActivationContext;
private static IntPtr hActCtx;
private static bool contextCreationSucceeded = false;
public EnableThemingInScope(bool enable)
{
cookie = 0;
if (enable && OSFeature.Feature.IsPresent(OSFeature.Themes))
{
if (EnsureActivateContextCreated())
{
if (!ActivateActCtx(hActCtx, out cookie))
{
// Be sure cookie always zero if activation failed
cookie = 0;
}
}
}
}
~EnableThemingInScope()
{
Dispose(false);
}
void IDisposable.Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (cookie != 0)
{
if (DeactivateActCtx(0, cookie))
{
// deactivation succeeded...
cookie = 0;
}
}
}
private bool EnsureActivateContextCreated()
{
lock (typeof(EnableThemingInScope))
{
if (!contextCreationSucceeded)
{
// Pull manifest from the .NET Framework install
// directory
string assemblyLoc = null;
FileIOPermission fiop = new FileIOPermission(PermissionState.None);
fiop.AllFiles = FileIOPermissionAccess.PathDiscovery;
fiop.Assert();
try
{
assemblyLoc = typeof(Object).Assembly.Location;
}
finally
{
CodeAccessPermission.RevertAssert();
}
string manifestLoc = null;
string installDir = null;
if (assemblyLoc != null)
{
installDir = Path.GetDirectoryName(assemblyLoc);
const string manifestName = "XPThemes.manifest";
manifestLoc = Path.Combine(installDir, manifestName);
}
if (manifestLoc != null && installDir != null)
{
enableThemingActivationContext = new ACTCTX();
enableThemingActivationContext.cbSize = Marshal.SizeOf(typeof(ACTCTX));
enableThemingActivationContext.lpSource = manifestLoc;
// Set the lpAssemblyDirectory to the install
// directory to prevent Win32 Side by Side from
// looking for comctl32 in the application
// directory, which could cause a bogus dll to be
// placed there and open a security hole.
enableThemingActivationContext.lpAssemblyDirectory = installDir;
enableThemingActivationContext.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;
// Note this will fail gracefully if file specified
// by manifestLoc doesn't exist.
hActCtx = CreateActCtx(ref enableThemingActivationContext);
contextCreationSucceeded = (hActCtx != new IntPtr(-1));
}
}
// If we return false, we'll try again on the next call into
// EnsureActivateContextCreated(), which is fine.
return contextCreationSucceeded;
}
}
// All the pinvoke goo...
[DllImport("Kernel32.dll")]
private extern static IntPtr CreateActCtx(ref ACTCTX actctx);
[DllImport("Kernel32.dll")]
private extern static bool ActivateActCtx(IntPtr hActCtx, out uint lpCookie);
[DllImport("Kernel32.dll")]
private extern static bool DeactivateActCtx(uint dwFlags, uint lpCookie);
private const int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;
private struct ACTCTX
{
public int cbSize;
public uint dwFlags;
public string lpSource;
public ushort wProcessorArchitecture;
public ushort wLangId;
public string lpAssemblyDirectory;
public string lpResourceName;
public string lpApplicationName;
}
}
Then replace the default Main() with the following wrapper code. This procedure pushes a themed activation context before creating any controls:
static void Main()
{
using( new EnableThemingInScope( true ) )
{
Form form1 = new Form();
form1.CreateControl();
Application.Run(form1);
}
}
...Enjoy !!
Comments
- Anonymous
August 18, 2004
I solved my theme problems another way, and it hasn't given me problems since :)
Here is a discussion and a sample with the solution (last post).
http://www.syncfusion.com/Support/Forums/message.aspx?MessageID=9503 - Anonymous
August 19, 2004
The comment has been removed - Anonymous
August 19, 2004
Russ, try to open a form with .ShowDialog() then close it and see what happens :) - Anonymous
May 30, 2009
PingBack from http://outdoorceilingfansite.info/story.php?id=2180 - Anonymous
June 11, 2009
The comment has been removed