Excel 2016 VSTO Addin With Native Code DLL - c#

Creating an Excel addin using an SDK from a 3rd party. The SDK contains a native code DLL (in both 32 and 64 bit versions). My addin code is in C# and it appears that addins only run if compiled under "AnyCPU" option.
When I attempt to run the app I get "An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)" which usually means a 64/32 mismatch, and the error is from attempting to load the 3rd party native code DLL.
My question is: are my assumptions correct about AnyCPU and is there a way to run native code DLL's from an addin compiled under AnyCPU? Thanks!

before .net 4.5 the AnyCPU depends on your current compile machine.
if your machine is 32bit CPU, AnyCPU will compile your program as 32bit program. if it's 64bit CPU, i t will compile as 64bits.
after .net 4.5, the AnyCPU changed:
If the process runs on a 32-bit Windows system, it runs as a 32-bit
process. IL is compiled to x86 machine code.
If the process runs on
a 64-bit Windows system, it runs as a 32-bit process. IL is compiled
to x86 machine code.
If the process runs on an ARM Windows system,
it runs as a 32-bit process. IL is compiled to ARM machine code.
If you want your program can run both 32bit and 64bit cpu, you should link your native code DLL with 32 bit version, otherwise you need to compile them in separate CPUs

Yes it is possible, you can create some wrappers functions that redirect the call to the native dll depending on the current architecture , example :
[DllImport("bin32\\Native86Dll", EntryPoint = "MyFunc", CharSet = CharSet.Ansi, ExactSpelling = true)]
public static extern int MyFunc_32(string sCommand);
[DllImport("bin64\\Native64Dll", EntryPoint = "MyFunc", CharSet = CharSet.Ansi, ExactSpelling = true)]
public static extern int MyFunc_64(string sCommand);
public static int MyFunc(string sCommand )
{
return System.Environment.Is64BitProcess ? MyFunc_64(sCommand) : MyFunc_32(sCommand);
}

Related

DriverPackagePreinstall to work on 64 bit (compiled to 32 bit)

Is there a way I can compile an application which calls the DriverPackagePreinstall() (using Pinvoke) in such a way that it can work on 64-bit devices (Windows 7) even though it's targeted to 32bit?
The reason being it will be run as part of an installer of a much bigger application (using Windows Installer Project), which will target 32 bit but must run on a 64 bit platform also.
Here is my code:
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace MyDriver
{
class Program
{
static void Main(string[] args)
{
if (args.Count() == 0)
{
Console.WriteLine("Please specify filename!");
return;
}
int result= DriverPackagePreinstall(args[0], 0);
if (result == 0)
{
Console.WriteLine("Success");
} else {
Console.WriteLine("Error: 0x{0}", result.ToString("X8"));
}
}
[DllImport("DIFxAPI.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Int32 DriverPackagePreinstall(
[MarshalAs(UnmanagedType.LPTStr)] string DriverPackageInfPath,
Int32 Flags);
}
}
}
If I build this targeting x86, and try to run on a 64-bit machine, I get error E0000235 (ERROR_IN_WOW64). This error goes away if I build targeting x64.
Now I don't mind compiling it twice and letting the installer decide which to install based on the platform it's being installed to. However, if I try to build the installer while including the 64-bit version I get the error,
263 File 'MyDriver.x64.exe' targeting 'AMD64' is not compatible with the project's target platform 'x86'
Alternatively a way of getting the Installer to overlook this error at build time (and run it anyway when the project is installing) would be good.
I worked around this by adding both the 64-bit and 32-bit versions of the application to a self-extracting zip-file together with a batch file which decides which one to execute.
The self-extractor is a 32-bit exe so as long no one tells Visual Studio that it contains a 64-bit one, it works.

Exception from HRESULT: 0x8007000B, for a MatLab Compiled dll

I received a Matlab Compiled .dll (targeted for a 32 bit (x86) system). I use a 64 bit system with Windows 7 on it. I am using Visual Studio IDE for making my application and import this .dll. Below is the Code.
[DllImport("Generate_Curve.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Generate_Curve", CharSet = CharSet.Ansi)]
I have seen a similar question .net Framework Error (HRESULT 0x8007000B) so I tried to change the Platform Target to x86 or Any CPU coupled with Prefer 32-bit. Still there is an Error thrown An unhandled exception of type 'System.BadImageFormatException with Additional Information as An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B). Can any one point out something that I have missed.
I received the answer from Matlab (Mathworks) that there is a bug in Matlab Compiler. If one compiles from a 64 bit Matlab in Matlab Coder for a target machine or application of 32 bit the .dll generated is highly likely to be corrupted. The solution is to compile a dll for 32bit target machine/application using a 32 bit Matlab (Matlab Coder). This issue is more of a Matlab Coder issue.

"Could not find a part of the path," on path that does exist

I am attempting to update the lock screen background like this:
string filename = #"C:\app\screenshot.temp.jpg";
string finalLocation = #"C:\Windows\System32\oobe\info\backgrounds\backgroundDefault.jpg";
File.Move(filename, finalLocation);
Unfortunately this throws a System.IO.DirectoryNotFoundException exception:
An unhandled exception of type 'System.IO.DirectoryNotFoundException' occurred in mscorlib.dll
Additional information: Could not find a part of the path.
However when i browse to C:\Windows\System32\oobe\info\backgrounds in Windows Explorer, CMD, or Powershell it does exist. I also have the security to write, rename, and delete files in that location (and the C# process is running in my context). What is going on?
If you've encountered this, I am guessing you're executing the process on a 64 bit version of Windows.
Background:
On Windows 32 bit, there is a single System32 folder called "System32" that stores all 32 bit DLLs. On Windows 64 bit, there are two "System32" folders one still called System32 and another called SysWOW64.
These two folders store the opposite of what their names imply:
System32 stores 64 bit DLLs.
SysWOW64 stores 32 bit DLLs.
SysWOW64 stands for "Windows 32-bit on Windows 64-bit." So it is a folder that exists for backwards compatibility with 32 bit for 32 bit processes.
Why this breaks things?
Microsoft are obsessed with backwards compatibility, so when they added 32 bit emulation on 64 bit Windows, they wanted to make the bitness of the system invisible to the 32 bit processes running, and they introduced a bunch of compatibility shims (fixes).
One of these shims redirects IO requests for %WINDIR%\System32 to %WINDIR%\SysWOW64 for processes running in 32 bit mode only.
So when you request a move from:
C:\Windows\System32\oobe\info\backgrounds\backgroundDefault.jpg
Windows may actually instead request the move from:
C:\Windows\SysWOW64\oobe\info\backgrounds\backgroundDefault.jpg
Which does not exist. Thus explaining the error you're seeing.
The Fix
The easiest fix is to change your program to build as a 64 bit process. You can do this by:
Right Click on the Project -> Properties -> Build [Tab] -> Platform target -> x64
Now when you run, requests against %WINDIR%\System32 should actually hit %WINDIR%\System32 for real.
Alternatively if you need to run your process in 32 bit mode (e.g. due to library compatibility) you can ask Windows to disable the shim like this:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
private static void Main(string[] args)
{
IntPtr ptr = new IntPtr();
bool isWow64FsRedirectionDisabled = Wow64DisableWow64FsRedirection(ref ptr);
}
In either case requests should be handled more literally by the operating system, and you can update the lock screen background (or any other operation in System32).

Mono interop: Loading 32bit shared library does not work on my 64bit system

i have a problem running a simple interop example on my system.
I built a simple 32-bit shared library called libtest.so (c++)
g++ -c -fpic test.cpp -m32
g++ -shared -fpic -o libtest.so test.o -m32
My System:
Ubuntu Linux 10.04 x86_64
Mono C# compiler version 2.4.4.0
In addition i have a sample c# program using my shared library:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
public class Test
{
[DllImport("libdl.so")]
static extern IntPtr dlopen(string filename, int flags);
[DllImport("libdl.so")]
static extern IntPtr dlclose(IntPtr handle);
[DllImport ("./libtest.so")]
private static extern void HelloWorld();
[DllImport ("./libtest.so",EntryPoint="Test")]
private static extern int Testl(int a,int b);
public static int Main(string[] args)
{
IntPtr handle = dlopen("./libtest.so",2);
if(handle == IntPtr.Zero)
{
Console.WriteLine("Error loading shared library");
return -1;
}
HelloWorld();
int ret = Testl(116,1);
Console.WriteLine("Result from shared-Librarry Call: " + ret);
dlclose(handle);
return 0;
}
}
The Problem: Loading the library does not work.
exporting MONO_LOG_LEVEL=debug gives me the following hint:
Mono-INFO: DllImport error loading library './libtest.so: Wrong ELF-Class: ELFCLASS32'.
Well i guess mono runs my program in 64-bit mode and therefore it cannot call a 32-bit shared library? If i build the shared library in 64 bit mode (without -m32) everything works fine!!
My Mono-Compiler 2.4.4. does not have the option to specify the platform with /platform:x86 and therefore i installed version 2.10, but using it does not work either.
/opt/mono-2.10/bin/gmcs /platform:x86 sharpCall.cs
Is there a possibility to load 32-bit shared libraries on a 64-bit system?
The problem is that you have a 64bit version of Mono installed on your system which can only P/Invoke into 64bit native libraries, it cannot P/Invoke into 32bit native libraries.
The -platform:x86 flag is meant for the C# compiler, not the runtime, and does not hint to the runtime to use a 32bit memory space.
You need to install the 32bit version of Mono on your Ubuntu system if you want to P/Invoke into 32bit native libraries.
You cannot load a 32 bit module into a 64 bit process. Either run a 32 bit process, or compile your native module as a 64 bit module.
Is there a possibility to load 32-bit shared libraries on a 64-bit
system?
Yes, but only if you compile the program that uses said shared libraries into a 32-bit process.
Well i guess mono runs my program in 64-bit mode and therefore it
cannot call a 32-bit shared library? If i build the shared library in
64 bit mode (without -m32) everything works fine!!
Of course this happen. Just compile the program with the m32 flag and you should have no problems.

COM cannot start out-of-process .Net server compiled as AnyCPU

I am trying to get COM to start my out-of-process .NET COM server. It works if the server process is compiled with x64, but if I use AnyCPU (which is what I want) then it hangs for a while and eventually fails with 0x80080005 (CO_E_SERVER_EXEC_FAILURE). How can I get this to work?
I am running on a 64-bit machine: Windows 7 with Visual Studio 2008 SP1.
I can see in Task Manager that it does start my server. So I guess the problem is in the communications between COM and the server (class registration).
My test client application is written in C#, but it doesn't matter whether it is compiled for x86 or x64. The problem also occurs with something written in 32-bit C++.
If I rebuild the server using x64 and run it, and then rebuild back as AnyCPU, then COM can start it. A reboot will take me back to the original situation. Perhaps COM doesn't know in advance what bitness is going to be used, and a previous execution helps.
I found Andy McMullen's blog post and tried passing CLSCTX_ACTIVATE_64_BIT_SERVER to CoCreateInstance(), but that triggers a failure earlier: 0x80040154 (REGDB_E_CLASSNOTREG). Am I doing something wrong in my COM registration? You can see below that it is very simple. Registration occurs when running in 64 bits, and the problem occurs when the client is 64 bits, so Wow6432Node should not be involved.
Another chap has had a similar problem, but the MSFT answer is confusing. He seems to be suggesting it can only work via DCOM (see link) or COM+. I suspect either will be an awful lot of work, and substantially worse than distributing my .exe built as x64 and x86.
You may be wondering why I am implementing IPersistFile. It is because my real problem is to get BindMoniker() working from a 32-bit C++ program to my AnyCPU .Net program. I have reduced my problem to the simpler example presented here.
Here is the client code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface)]
static extern object CoCreateInstance(
[In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
[MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter,
CLSCTX dwClsContext,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
[Flags]
enum CLSCTX : uint
{
CLSCTX_LOCAL_SERVER = 0x4,
CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000,
}
private void Form1_Load(object sender, EventArgs e)
{
IPersistFile pf = (IPersistFile)CoCreateInstance(
new Guid("1984D314-FC8D-44bc-9146-8A13500666A6"),
null,
CLSCTX.CLSCTX_LOCAL_SERVER,
new Guid("0000010b-0000-0000-C000-000000000046")); // IPersistFile
pf.Load("c:\\bozo", 0);
}
}
and here is the server:
static class Program
{
[STAThread]
static void Main()
{
if (Environment.CommandLine.Contains("/reg")) {
RegistryKey cls = Registry.LocalMachine.CreateSubKey(String.Format(
"SOFTWARE\\Classes\\CLSID\\{0}", PersistFile.ClassID.ToString("B")));
cls.SetValue("InprocHandler32", "Ole32.dll");
RegistryKey ls32 = cls.CreateSubKey("LocalServer32");
ls32.SetValue(null, '"' + Application.ExecutablePath + '"');
ls32.SetValue("ServerExecutable", Application.ExecutablePath);
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
RegistrationServices reg = new RegistrationServices();
reg.RegisterTypeForComClients(
typeof(PersistFile),
RegistrationClassContext.LocalServer,
RegistrationConnectionType.MultipleUse);
Application.Run(new Form1());
}
}
[ComVisible(true),
Guid("1984D314-FC8D-44bc-9146-8A13500666A6"),
ClassInterface(ClassInterfaceType.None)]
public class PersistFile : IPersistFile
{
public static Guid ClassID
{
get
{
GuidAttribute a = (GuidAttribute)typeof(PersistFile).GetCustomAttributes(typeof(GuidAttribute), false)[0];
return new Guid(a.Value);
}
}
#region IPersistFile
public void GetClassID(out Guid pClassID)
{
MessageBox.Show("GetClassID");
pClassID = ClassID;
}
public int IsDirty()
{
MessageBox.Show("IsDirty");
return 1;
}
public void Load(string pszFileName, int dwMode)
{
MessageBox.Show(String.Format("Load {0}", pszFileName));
}
public void Save(string pszFileName, bool fRemember)
{
MessageBox.Show("Save");
throw new NotImplementedException();
}
public void SaveCompleted(string pszFileName)
{
MessageBox.Show("SaveCompleted");
throw new NotImplementedException();
}
public void GetCurFile(out string ppszFileName)
{
MessageBox.Show("GetCurFile");
throw new NotImplementedException();
}
#endregion
}
I guess the problem is at run-time. I have created a COM Server which registers using a C++ library (registration is performed flawlessly). I have run into problems when switching to AnyCPU from .NET (CS).
The architecture:
C++ library interfacing COM (built on both x64 and x86 platforms)
.NET library wrapper (CS) (correctly instantiates the required x64/x86 C++ library)
.NET application (CS) - COM client or COM server
Ugly things happen when registering the .NET application built as "AnyCPU". Once the COM Client invokes the COM Server through DCOM, the server application starts but the client receives error that the COM Server could not be started.
I went some steps further, analyzed registration data with procmon and other tools and I reached the same conclusion:
x86 registers the classes in CLASSES\Wow6432Node
x64 and AnyCPU register the classes in CLASSES (on a x64 windows machine, exactly the same keys; I bet that x86 and AnyCPU would register the same on an x86 machine)
Now, I did some more experiments: the x86/x64/AnyCPU COM Client can connect with no problems to any x86/x64 COM Server but it cannot connect anyhow to an AnyCPU COM Server...
I then performed the following test cases:
Have the x86 COM Server register, replace the executable with the AnyCPU COM Server: COM Client was starting the x86 COM Server, but no communication... it was starting the server over and over again..
Have the x64 COM Server register, replace the executable with the AnyCPU COM Server: COM Client was starting the x64 COM Server, but no communication... it was starting the server over and over again..
Have the AnyCPU COM Server register, replace the executable with the x86 COM Server: COM Client was able to successfully start the and connect to the x86 COM Server.
Have the AnyCPU COM Server register, replace the executable with the x64 COM Server: COM Client was able to successfully start the and connect to the x64 COM Server.
Have the x86 COM Server register, replace the executable with the x64 COM Server: COM Client was able to successfully start the and connect to the x64 COM Server.
Have the x64 COM Server register, replace the executable with the x86 COM Server: COM Client was able to successfully start the and connect to the x86 COM Server.
Where the hell is the communication problem? This is very odd... None of the presented solutions (CLSCTX_ACTIVATE_64_BIT_SERVER, PreferredServerBitness or corflags) helped.
Anyone else did some progress on this matter? Should we contact Microsoft?
Try to use the RegistrationServices class to register your com assembly. It will also choose the correct registry pathes and do some other things.
Example:
Assembly currentAssembly = Assembly.GetExecutingAssembly();
System.Runtime.InteropServices.RegistrationServices regAsm = new System.Runtime.InteropServices.RegistrationServices();
bool isRegistered = regAsm.RegisterAssembly(currentAssembly, System.Runtime.InteropServices.AssemblyRegistrationFlags.SetCodeBase);
Also I think, that .NET client assemblies have some trouble according to .NET com servers, but I can't find any resource for it...
Hope, it will help...

Categories

Resources