I have a 32-bit app that makes use of Java Accessibility (WindowsAccessBridge-32.dll, via the Java Access Bridge), and works perfectly on a 32-bit machine, but fails on an x64 machine.
I believe I have tracked it down to one of the first calls after Windows_run:
getAccessibleContextFromHWND(hwnd, out vmId, out context)
defined as follows:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("WindowsAccessBridge-32.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static bool getAccessibleContextFromHWND(IntPtr hwnd, out Int32 vmID, out IntPtr acParent);
This call works fine on the 32-bit system, returning True, populating both vmId (with some 5-digit value, which), and context - whereas on the 64-bit system, it returns True, populates 'context', but returns '0' for vmId.
If I assume that 0 is valid (even though it's a random 5-digit number resembling a pointer on the 32-bit system), the next call still fails:
AccessibleContextInfo aci = new API.AccessibleContextInfo();
if (!getAccessibleContextInfo(vmId, context, ref aci))
throw new Exception();
where:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("WindowsAccessBridge-32.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static bool getAccessibleContextInfo(Int32 vmID, IntPtr ac, ref AccessibleContextInfo info);
(I'm omitting the AccessibleContextInfo struct for brevity, but I can provide it if necessary).
I know that the libraries are working, because both JavaMonkey and JavaFerret work correctly. Furthermore, call to isJavaWindow works, returning 'true', or 'false' as appropriate, and I am linking to the correct DLL (WindowsAccessBridge-32).
Can anyone suggest what may be wrong here?
It appears that the problem is in the type of AccessibilityContext:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("WindowsAccessBridge-32.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static bool getAccessibleContextFromHWND(IntPtr hwnd, out Int32 vmID, out IntPtr acParent);
AccessibilityContext (acParent above), which I had incorrectly mapped as an IntPtr, is actually an Int32 when using the "legacy" WindowsAccessBridge.dll library (used under x86), and an Int64 when using the WOW64 WindowsAccessBridge-32.dll library.
So the upshot is, the code has to differ between x86 and WOW x64, and must be compiled separately for each. I do this by #define'ing WOW64 during x64 builds, always referencing the Int64 methods, and using "shim" methods on x86:
#if WOW64 // using x64
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("WindowsAccessBridge-32.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static bool getAccessibleContextFromHWND(IntPtr hwnd, out Int32 vmID, out Int64 acParent);
#else // using x86
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("WindowsAccessBridge.dll", EntryPoint = "getAccessibleContextFromHWND", CallingConvention = CallingConvention.Cdecl)]
private extern static bool _getAccessibleContextFromHWND(IntPtr hwnd, out Int32 vmID, out Int32 acParent);
public static bool getAccessibleContextFromHWND(IntPtr hwnd, out Int32 vmID, out Int64 acParent)
{
Int32 _acParent;
bool retVal = _getAccessibleContextFromHWND(hwnd, out vmID, out _acParent);
acParent = _acParent;
return retVal;
}
#endif
If your using a 64 bit JVM with a 32 bit version of the Java Access bridge it won't work correctly. You need a 64 bit version of the access bridge which has recently been released. see
http://blogs.oracle.com/korn/entry/java_access_bridge_v2_0
For instructions on installing a 32 bit copy of the access bridge for use with 32 bit JRE's under 64 bit windows see
http://www.travisroth.com/2009/07/03/java-access-bridge-and-64-bit-windows/
The call to 'initializeAccessBridge' REQUIRES you to have an active windows message pump.
Inside 'initializeAccessBridge', it (eventually) creates a hidden dialog window (using CreateDialog). Once the dialog is created, it performs a PostMessage with a registered message. The JavaVM side of the access bridge responds to this message, and posts back another message to the dialog that was created (it appears to be a 'hello' type handshake between your app and the java VM). As such, if your application doesn't have an active message pump, the return message from the JavaVM never gets received by your app.
Related
I'm building a c# class library that calls third party DLLs supplied by a vendor we're working with. The vendors examples are all in vc++ and are running and working.
I'm trying to load one of the DLLs and it's returning an IntPtr 0. When I call Marshal.GetLastWin32Error() I get 193 which from what I've read means that I'm trying to load a 32-bit DLL in 64-bit app. But I checked again and again and both my class library is set to x86 and my console application that makes the call to that class library is set to x86.
I can successfully load other DLL files by the same vendor (which are also 32-bit).
This is my Native helper class:
class NativeHelper
{
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary", SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryEx", SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr hFile, LoadLibraryFlags flags);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
[System.Flags]
public enum LoadLibraryFlags : uint
{
DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008,
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000
}
}
And this is how I make the call:
IntPtr pDll = NativeHelper.LoadLibrary(#"dhplay.dll"); // returns 0
if (pDll == IntPtr.Zero)
{
var err = Marshal.GetLastWin32Error().ToString(); // returns 193
Console.WriteLine(err);
}
What am I missing here? If I'm running x86 why is that DLL not loaded and others do?
Edit (Additional information):
IntPtr.Size is 4
dumpbin returns 8664 machine (x64) - That means it's 64-bit? I checked for other DLL used and they are 8664 machine (x86)
As far as dependencies, it's working on a vc++ application on my machine.
If there is smoke, there is often fire. Some dll is probably x64 while your app is x32 (or the reverse).
Check in you app IntPtr.Size. 4 → x86, 32 bits, 8 → x64, 64 bits
If you use the Visual Studio Command Prompt, you should be able to use dumpbin. Do a dumpbin /headers yourdll.dll | more for your dlls. One of the first rows should be machine (...). In the (...) it should be written if it is x86 (so 32 bits) or x64 (so 64 bits)
To the second question, you can't in any way mix 32 and 64 bit dlls in a single process. Some programs "cheat" and create a secondary process with the correct bits to load the dlls and then do IPC (inter-process-communication) between the primary process and the secondary process to use the dll. Clearly it is complex.
I am running a Web API Asp.Net 4.6 app running in Any CPU mode (though I've also tried this specifying x64) and it makes a call to an unmanaged x64 C dll. This works fine when running within Visual Studio (using default IIS Express settings) though I get Windows Error 126 (The specified module could not be found.) when I deploy to another server and run in IIS or IIS Express even though I am sure that the path to the DLL is correct. Is there something else I can try?
My Native Methods Wrapper:
public static class NativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
}
My Load DLL Method:
private static void LoadDll()
{
UnloadDLL(); //Unload the module first
if (string.IsNullOrEmpty(DllDirectory))
throw new ApplicationException("DPI DLL directory not specified.");
if (!File.Exists(DllPath))
throw new ApplicationException("Could not find DPI DLL.");
pDll = NativeMethods.LoadLibrary(DllPath);
//Fails Here
if (pDll == IntPtr.Zero)
throw new ApplicationException(string.Format("Failed to load library <{0}> (ErrorCode: {1})", DllPath, Marshal.GetLastWin32Error()));
}
Let me know if I can make anything else more clear, thanks!
Use Dependency Walker to get the list of module's dependencies (module with path DllPath). Also check do you need install Visual C++ Redistributable on the server.
By ikenread: Need to check unmanaged DLL compilation mode. If it compiled in Debug mode, it will be dependent on Debug versions of Visual C++ Redistributable dlls and they will be absent (most likely) on the production server.
I am using the Hardware ID Extractor library (written in Delphi) from http://www.soft.tahionic.com/download-hdd_id/index.html with the purpose of generating unique system fingerprints.
The library is really good and unlike anything else I have seen on the market, but the main issue with it is that it's unstable when running with .NET applications, meaning that it sometimes works, other times it works for a few function calls then the main application crashes, or most of the time the application instantly crashes when a dll function is being called.
As the developer of the library pointed out (in the last support e-mail that I have received), the fault is with ntdll.dll, as I have seen that for myself:
Following is a link to a demo project I have created with the purpose of testing the dll functions (so to make sure that nothing else interferes, the demo app does that and only that- it calls the dll functions).
http://www.mediafire.com/download/1jws7zh9218v88a/HardwareIdExtractDllTest.zip
The archive contains the Visual Studio 2013 project with source code and a compiled demo application which looks like this:
The list of functions contained by the dll can be found here:
http://www.soft.tahionic.com/download-hdd_id/hardware%20id%20programming%20source%20code/exported%20functions%20for%20non-Delphi.html
If anyone has the knowledge and is willing to test the demo project/application to make tests or personal opinions in regard to the issue, and then share a possible solution with me, I would be grateful.
Please let me know if there's anything I can do to further assist in solving this issue if you think there's anything that can be done about it.
EDIT: This is how I am declaring the dll functions
[DllImport("HardwareIDExtractorC.dll")]
private static extern bool EnterKey(int key);
[DllImport("HardwareIDExtractorC.dll")]
private static extern bool IsCPUIDAvailable();
[DllImport("HardwareIDExtractorC.dll")]
private static extern int GetCPUCount();
[DllImport("HardwareIDExtractorC.dll")]
private static extern byte CoreNumber2CoreMask(byte cpuCore);
[DllImport("HardwareIDExtractorC.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "GetCPUID")]
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string GetCPUID(byte cpuCore);
[DllImport("HardwareIDExtractorC.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "GetCpuIdNow")]
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string GetCpuIdNow();
[DllImport("HardwareIDExtractorC.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "GetIDESerialNumber")]
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string GetIDESerialNumber(byte driveNumber);
The functions that fail are the ones that return string. Such a p/invoke marshals the return value as a pointer to null terminated character array, and then calls CoTaskMemFree on the raw pointer returned by the unmanaged code.
With probability close to 1 that string was not allocated on the COM heap and so the error is likely in the C# p/invoke declarations. That an access violation then arises in ntdll is quite plausible.
So, to make progress you need to fix the p/invoke calls. I cannot tell you how to do so because you have not shown the unmanaged function declarations. Or, more importantly, how the memory is allocated.
There are some clues at the documentation
procedure ReleaseMemory (P: PAnsiChar); stdcall;
I think this tells us that the strings returned by the DLL must be deallocated by the DLL, by calling this function. Presumably because they were allocated by the DLL's heap allocator.
So, use IntPtr for the return type of the functions that return text. Call Marshal.PtrToStringAnsi to convert to a C# string. And then pass the pointer to ReleaseMemory.
For example:
[DllImport(DllPath, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetCPUID(ushort CoreMask);
[DllImport(DllPath, CallingConvention = CallingConvention.StdCall)]
private static extern void ReleaseMemory(IntPtr P);
....
IntPtr ptr = GetCPUID(CoreMask);
string cpuid = Marshal.PtrToStringAnsi(ptr);
ReleaseMemory(ptr);
This question already has answers here:
Target 32 Bit or 64 Bit native DLL depending on environment
(3 answers)
DllImport - An attempt was made to load a program with an incorrect format [duplicate]
(1 answer)
Closed 8 years ago.
The community reviewed whether to reopen this question 8 months ago and left it closed:
Original close reason(s) were not resolved
I want my C# application to conditionally run a native method, conditionally choosing to run either the x86 or the x64 version of the dll. Whenever I try to load the 32 bit dll I get the below error:
Unhandled Exception: System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
at <exeName>.MiniDumpMethods.MiniDumpWriteDumpX86(IntPtr hProcess, UInt32 processId, SafeHandle hFile, MINIDUMP_TYPE dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam)
Background context: I want my binary to take a memory dump of a given process. Based on whether or not the process it's taking a memory dump of is 32 or 64 bit it'll choose to run the MiniDumpwriteDump method from the x86 or x64 version of dbghelp.dll.
I'm currently doing the following:
[SuppressUnmanagedCodeSecurity]
internal static class MiniDumpMethods
{
[DllImport("dbghelp.dll",
EntryPoint = "MiniDumpWriteDump",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode,
ExactSpelling = true,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool MiniDumpWriteDump(
IntPtr hProcess,
uint processId,
SafeHandle hFile,
MINIDUMP_TYPE dumpType,
IntPtr expParam,
IntPtr userStreamParam,
IntPtr callbackParam);
[DllImport("dbghelpx86.dll",
EntryPoint = "MiniDumpWriteDump",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode,
ExactSpelling = true,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool MiniDumpWriteDumpX86(
IntPtr hProcess,
uint processId,
SafeHandle hFile,
MINIDUMP_TYPE dumpType,
IntPtr expParam,
IntPtr userStreamParam,
IntPtr callbackParam);
}
Any idea how I can conditionally load either the x86 or the x64 version of the dll?
(Note: dbghelpx86.dll is the x86 version of dbghelp.dll that I renamed)
Thanks
You cannot load a 32 bit DLL into a 64 bit process. To support this you will have to have two different EXE's, one compiled as 64 bit and one compiled as 32 bit.
If you run the 64 bit process and encounter a 32 bit dump, you'll have to launch the 32 bit version of the EXE to process the dump file. Once it is processed you can use some sort of IPC (Interprocess Communication) mechanism to send the results back to the 64 bit process.
I've noticed something very strange. I was trying to call the CRT function "putchar", and was unable to get it to work. So I double-checked that I wasn't missing something, and I copied the code directly from the P/Invoke tutorial on MSDN to see if it worked.
http://msdn.microsoft.com/en-us/library/aa288468%28VS.71%29.aspx
You'll notice that they import "puts".
So I tested the exact code copied from MSDN. It didn't work! So now I got frustrated. I've never had this problem before.
Then I just so happened to run WITHOUT debugging (hit ctrl+f5), and it worked! I tested other functions which output to the console too, and none of them work when debugging but all work when not debugging.
I then wrote a simple C dll which exports a function called "PrintChar(char c)". When I call that function from C#, it works even if I'm debugging or not, without any problems.
What is the deal with this?
The Visual Studio hosting process is capable of redirecting console output to the Output window. How exactly it manages to do this is not documented at all, but it gets in the way here. It intercepts the WriteFile() call that generates the output of puts().
Project + Properties, Debug tab, untick "Enable the Visual Studio hosting process". On that same page, enabling unmanaged debugging also fixes the problem.
It's a bad example, using the C-Runtime Library DLL to call puts. Keep reading the tutorial as there is good info there, but try making Win32 API calls instead.
Here is a better introduction to p/invoke: http://msdn.microsoft.com/en-us/magazine/cc164123.aspx
It's old, but the information is still good.
Edited
My explaination was wrong.
I went looking for a correct explaination and I discovered that the C-Runtime puts method and the .NET Framework Console.Write method differ in how they write to the console (Console.Write works where the p/invoke to puts does not). I thought maybe the answer was in there, so I whipped up this demonstration:
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
class Program
{
public static void Main()
{
int written;
string outputString = "Hello, World!\r\n";
byte[] outputBytes = Encoding.Default.GetBytes(outputString);
//
// This is the way the C-Runtime Library method puts does it
IntPtr conOutHandle = CreateFile("CONOUT$", 0x40000000, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
WriteConsole(conOutHandle, outputBytes, outputString.Length, out written, IntPtr.Zero);
//
// This is the way Console.Write does it
IntPtr stdOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
WriteFile(stdOutputHandle, outputBytes, outputBytes.Length, out written, IntPtr.Zero);
// Pause if running under debugger
if (Debugger.IsAttached)
{
Console.Write("Press any key to continue . . . ");
Console.ReadKey();
}
}
const int STD_OUTPUT_HANDLE = -11;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
static extern int WriteFile(IntPtr handle, [In] byte[] bytes, int numBytesToWrite, out int numBytesWritten, IntPtr mustBeZero);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, FileShare dwShareMode, IntPtr securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
static extern bool WriteConsole(IntPtr hConsoleOutput, [In] byte[] lpBuffer, int nNumberOfCharsToWrite, out int lpNumberOfCharsWritten, IntPtr mustBeZero);
}
Both of those successfully output under the debugger, even with the hosting process enabled. So that is a dead end.
I wanted to share it in case it leads someone else to figuring out why it happens -- Hans?