I have a little project, where I want to bulk load some libraries referenced in the registry to read some resources. I get stuck on loading the library C:\Windows\System32\mmsys.cpl, which seems to have errors. The error itself is not the problem, but I want to prevent showing any native error dialog. I just want to redirect the exception in the native code to a exception of the managed code. I have tested this with .Net Framework 4.8 and .Net Core 3.1 as well as LoadLibrary and LoadLibraryEx in Kernel32 without any success. I have tested other *.cpl libraries and they are loading fine.
Expected:
Loading this library causes an exception thrown in the managed code - no error dialog is shown.
Actual:
Loading this library shows this annoying dialog as well as throwing the exception in the managed code.
[System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
private static extern System.IntPtr LoadLibrary(string lpFileName);
private static void Main(string[] args)
{
var resource = #"C:\Windows\System32\mmsys.cpl";
// var resource = #"C:\Windows\System32\timedate.cpl";
var lib = LoadLibrary(resource);
if (lib == System.IntPtr.Zero)
{
var errorCode = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
throw new System.SystemException($"System failed to load library '{resource}' with error {errorCode}");
}
}
The error dialog:
Update:
This snippet is now working for me:
[System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
private static extern System.IntPtr LoadLibrary(string lpFileName);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern ErrorModes SetErrorMode(ErrorModes uMode);
[System.Flags]
public enum ErrorModes : uint
{
SYSTEM_DEFAULT = 0x0,
SEM_FAILCRITICALERRORS = 0x0001,
SEM_NOALIGNMENTFAULTEXCEPT = 0x0004,
SEM_NOGPFAULTERRORBOX = 0x0002,
SEM_NOOPENFILEERRORBOX = 0x8000
}
private static void Main(string[] args)
{
SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS);
var resource = #"C:\Windows\System32\mmsys.cpl";
// var resource = #"C:\Windows\System32\timedate.cpl";
var lib = LoadLibrary(resource);
if (lib == System.IntPtr.Zero)
{
var errorCode = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
throw new System.SystemException($"System failed to load library '{resource}' with error {errorCode}");
}
}
You need to call first SetErrorMode with SEM_NOOPENFILEERRORBOX (0x8000).
See the Remarks section in the documentation for details:
To enable or disable error messages displayed by the loader during DLL loads, use the SetErrorMode function.
Related
I am working with a C++ DLL that I access with P/Invokes from C# code. The problem is I cannot redirect the std error that happens in the unmanaged side to a file. This works well if the build is in DEBUG but when the build is in RELEASE the STD logfile-unmanaged does not contain the error but contains and does not close the application, it keeps running like there was no error:
Before Error
After Error
In C++ the STD error is redirected to a file like this:
extern "C" __declspec(dllexport) void RedirectStd()
{
int fileNO = _fileno(stderr);
_close(fileNO);
int file = _open("logfile-unmanaged", O_CREAT | O_RDWR, 0644);
_dup2(file, fileNO);
}
A runtime error in C++ is generated like this:
extern "C" __declspec(dllexport) void DoException()
{
fprintf(stderr, "Before Error\n");
int a = 0;
int b = 10 / a;
fprintf(stderr, "After Error\n");
}
In C# I'm calling both of those methods:
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void RedirectStd();
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void DoException();
My Main function:
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
static void Main(string[] args) {
Console.WriteLine("===== REDIRECT =====");
Console.WriteLine("Native/Unmanaged exception redirected to logfile-unmanaged.");
RedirectStd();
Console.WriteLine("Native/Unmanaged std error redirected.");
Console.WriteLine("");
Console.WriteLine("===== EXCEPTION =====");
DoException();
Console.WriteLine("Waiting for program to crash");
do {
} while (true);
}
EDIT:
C# console application:
program.cs
using System;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;
namespace ConsoleWithErrors {
class Program {
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void RedirectStd();
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void DoException();
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
static void Main(string[] args) {
RedirectStd();
DoException();
Console.WriteLine("Waiting for program to crash");
do {
} while (true);
}
}
}
The console application stays opened like there was no error on the unmanaged side.
This works well if the build is in DEBUG but when the build is in RELEASE the STD logfile-unmanaged does not contain the error
It's probably optimized out, since you don't use the result of the division by zero. Try something like this instead:
extern "C" __declspec(dllexport) int DoException()
{
fprintf(stderr, "Before Error\n");
int a = 0;
int b = 10 / a;
fprintf(stderr, "After Error\n");
return b;
}
This will break the result out of internal linkage and force the compiler to emit the code.
You seem to be new to C# P/Invoke however, so here's a few tips:
the SetLastError attribute instructs the marshaller that the function will use the Win32 API SetLastError to set its error state, and your functions absolutely do not do so. You should remove it, because lying to the marshaller is never a recipe for success.
the SecurityCritical attribute has to do with process elevation between trust levels. Again, your application has nothing to do with that and should be removed.
the HandleProcessCorruptedStateExceptions attribute is deprecated since .Net Core (and including .Net 5). So if you're relying on it, you're nearing the end-of-life of your program, and it seems you barely even started writing it. The good news is that division by 0 isn't one of the exceptions that require this attribute to be processed, so again you should remove it!
On Windows, I use this code to determine whether the stdout stream is redirected for the currently running process:
private static bool? isOutputRedirected;
public static bool IsOutputRedirected
{
get
{
if (isOutputRedirected == null)
{
isOutputRedirected =
GetFileType(GetStdHandle(StdHandle.Output)) != FileType.FileTypeChar ||
!GetConsoleMode(GetStdHandle(StdHandle.Output), out _);
// Additional GetConsoleMode check required to detect redirection to "nul"
}
return isOutputRedirected == true;
}
}
private enum StdHandle : int
{
Input = -10,
Output = -11,
Error = -12,
}
private enum FileType : uint
{
FileTypeChar = 0x0002,
FileTypeDisk = 0x0001,
FileTypePipe = 0x0003,
FileTypeRemote = 0x8000,
FileTypeUnknown = 0x0000,
}
[DllImport("kernel32.dll")]
private static extern FileType GetFileType(IntPtr hFile);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(StdHandle nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
I have similar properties for the other two standard streams.
This is required to handle situations when displaying progress info or trying to update the current line with new content. This isn't useful when the output is being redirected, so the output should be reduced to more simple content.
But this fails as expected when running on Linux (dotnet publish -r linux-arm).
How can I determine the same situation on Linux? The web doesn't seem to know anything about it (yet).
For .NET Framework 4.5 and beyond, you might want to look at Console.IsOutputRedirected and Console.IsErrorRedirected
source: https://learn.microsoft.com/en-us/dotnet/api/system.console?view=netcore-3.1
I have a C# app thats going to run with admin privileges. I want to force DEP for it. Either set DEP On for the process or kill the app if DEP is disabled.
But I've failed in both.
My code is:
private static void CheckDEP()
{
var dep = SetProcessDEPPolicy(3);
var handle = Process.GetCurrentProcess().Handle;
var res = GetProcessDEPPolicy(handle, out var flags, out var permanent);
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetProcessDEPPolicy(uint dwFlags);
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
static extern bool GetProcessDEPPolicy(IntPtr hProcess, out uint lpFlags, out bool lpPermanent);
Calling the function SetProcessDEPPolicy(3) always fails with error 50 (documentation says that it should return this error only when it's called from 64bit process, but this is not the case).
And I probably have some bug in GetProcessDEPPolicy, because the call fails with error 87 (parameter is incorrect). Maybe the Handle is wrong?
So, the solution is to change the build configuration in Configuration Manager from AnyCPU to x86. Now both methods work as expected.
The errors are the same for AnyCPU as for x64 (where DEP is enforced by default and these methods should return the mentioned errors).
We have two web services Running on separate application pools in IIS7. The web services are identical, what differs is that one connects to the test Database and the other to the live database.
The Web services require a DLL which was written in Delphi to churn out some of the business logic required by the system, which we are loading using Dynamic DLL Loading like so:
public static class DynamicLinking
{
private static int libHandle;
private static string dllName;
public static void init(String pDllName)
{
dllName = pDllName;
libHandle = LoadLibrary(pDllName);
}
public static void fini()
{
FreeLibrary(libHandle);
}
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
static extern bool FreeLibrary(int hModule);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
delegate bool QryFunction(string domainString, string qryString,
int outputStringBufferSize, ref string outputStringBuffer);
public static void ExecuteDLLFunction(string pfunctionName, int bufferSize,
string pDomain, string inputXMLString,
out string outputString)
{
if (libHandle == 0)
throw new Exception(string.Format("Could not load library \"{0}\"", dllName));
var delphiFunctionAddress = GetProcAddress(libHandle, pfunctionName);
if (delphiFunctionAddress == IntPtr.Zero)
throw new Exception(string.Format("Can't find function \"{0}\" in library \"{1}\"", pfunctionName, dllName));
var queryFunction = (QryFunction)Marshal.GetDelegateForFunctionPointer(delphiFunctionAddress, typeof(QryFunction));
var outputStringBuffer = new String('\x00', bufferSize);
var errorMsgBuffer = new String('\x00', bufferSize);
if (!queryFunction(pDomain, inputXMLString,
bufferSize, ref outputStringBuffer))
throw new Exception(errorMsgBuffer);
outputString = outputStringBuffer;
}
}
Now the problem lies here: when we start the web the websites, which ever site gets started last will fail to load the dll, while the first one to load will run fine.
//edit---------------------------------
What's strange is that when the same DLL is copied in each website this issue arises however, if we link both sites to the same DLL, everything works fine
Any insight on this?
You should out each site in a separate App Pool, so that IIS will run each one in a its own process.
I am trying to set windows hooks in my program to an external EXE. This will be used to monitor resizing/minimizing of the window, so I can resize my program similarly, docking to the window.
How do I get around error codes 1428 and 126 below?
When calling SetWindowsHookEx with a null hMod, I was getting this error 1428. I get the same error if passing the current module (instead of IntPtr.Zero), which it seems to get correctly, as so:
IntPtr module = PInvoke.GetModuleHandle(null);
[...]
SetWindowsHookEx(...,...,module,...);
int error = PInvoke.GetLastError();
1428 = Cannot set nonlocal hook without a module handle
I also tried to grab the external program I'm hooking as a module using GetModuleHandle:
IntPtr module = PInvoke.GetModuleHandle("communicator.exe");
int error = PInvoke.GetLastError();
But error is then set to:
126 = The specified module could not be found.
I am using the following PInvoke statements:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId);
This is the procedure that is having the issue:
public void Install(IntPtr hWnd)
{
uint threadId;
uint processId;
if (hWnd == IntPtr.Zero)
{
threadId = (uint)AppDomain.GetCurrentThreadId();
throw new Exception("Lync thread not found!");
}
else
{
threadId = PInvoke.GetWindowThreadProcessId(hWnd, out processId);
}
//IntPtr module = PInvoke.GetModuleHandle(null);
//IntPtr module = PInvoke.GetModuleHandle(GetType().Module.FullyQualifiedName);
IntPtr module = PInvoke.GetModuleHandle("communicator.exe");
int error = PInvoke.GetLastError();
m_hhook = PInvoke.SetWindowsHookEx(
m_hookType,
m_filterFunc,
//Process.GetCurrentProcess().Handle,
//threadId);
//IntPtr.Zero,
//module,
//Marshal.GetHINSTANCE(
// System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]
// ).ToInt32()
module,
threadId);
//IntPtr hinst = Marshal.GetHINSTANCE(Process.GetCurrentProcess().Handle);
// http://msdn.microsoft.com/en-us/library/ms681385
// ERROR_HOOK_NEEDS_HMOD - 1428 = Cannot set nonlocal hook without a module handle
error = PInvoke.GetLastError();
}
You can't use GetModuleHandle for an external process. It must be a module that has been loaded into the current process.
I had the same issue: 126 = The specified module could not be found.
I added missing message loop into my app and it start working again.
I'm using Hook func like this:
hKeyboardHook = SetWindowsHookEx(
WH_KEYBOARD_LL,
KeyboardHookProcedure,
Marshal.GetHINSTANCE(typeof(your_class_type).Module),
0);
and I added Application.Run() at tne end of the Main func