Sharing environment variables across languages - c#

I'm writing an application composed by several modules written in different languages (i.e. Java, C#, C++).
I'm experiencing an odd behavior where the environment variables that I set in one module (e.g. C#) are not propagated to other modules.
As far as I understand, the problem is due to the fact that environment variables in Windows are accessed via the _environ struct in the runtime library, rather than via the process descriptor, hence libraries using different runtimes have different environment variables.
For C# in particular, this problem seems to occur only if I compile and run the code in Release, whereas compiling the code in Debug works just fine.
The code below reproduce the issue with two very simple modules written in C# and C++. I compiled the code with VS2015 Professional. C# code was compiled with runtime v4.0 and .NET framework v4.5.2
C# executable
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport("cpp_lib.dll", CharSet = CharSet.Unicode)]
public static extern void print_path();
static void Main(string[] args)
{
var path = Environment.GetEnvironmentVariable("PATH");
path += ";D:\\Temp";
Environment.SetEnvironmentVariable("PATH", path);
// Print the path from C#
Console.WriteLine("Path from C#: " + Environment.GetEnvironmentVariable("PATH"));
// Print the path from c++
print_path();
}
}
}
C++ library
#include <iostream>
#include <Windows.h>
extern "C" {
__declspec(dllexport) void print_path() {
std::cout << "PATH seen in C++: " << getenv("PATH") << std::endl;
}
}
As stated before, running the code in Debug the same path is printed from C# and C++, but running the code in Release results in the PATH printed from c++ missing the D:\Temp folder

User Environment variables are per-process and changes are not propagated to other processes.
To change system environment variables in your example PATH. You need to modify the value under the registry key HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment and then broadcast a WM_SETTINGCHANGE message. Applications wishing to see the change will have to handle the broadcast.
Source: https://learn.microsoft.com/en-us/windows/win32/procthread/environment-variables

It turns out Windows provide more functions to get environment variables.
From the documentation "getenv and _putenv use the copy of the environment pointed to by the global variable _environ to access the environment." whereas GetEnvironmentVariable "Retrieves the contents of the specified variable from the environment block of the calling process."
From these statements GetEnvironmentVariable looks like the safest alternative and replacing getenv with GetEnvironmentVariable indeed resolve the problem.
References:
https://social.msdn.microsoft.com/Forums/en-US/c27a6623-6f57-4c7e-be9b-6dd35d362872/sharing-environment-variables-across-languages?forum=windowsgeneraldevelopmentissues
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/getenv-wgetenv?view=vs-2019
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getenvironmentvariable

Related

Output Path in Visual Studio

I want to output my C# project into a user defined folder.
Therefore I tried the following:
1. Setting the output path in the properties
Setting Output path in properties for all configs and any CPU
I built in Debug|x64 mode the C# project.
The solution contains a second project in C++ which has a config type of DLL. Goal is to use the DLL in C++ in my C# project.
C++ Project properties
Property Page for C++ Project
Here is the respective code for:
C# Project
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace ConsoleApp
{
class Program
{
[DllImport("NativeLibrary.dll")]
public static extern void HelloWorld();
static void Main(string[] args)
{
HelloWorld();
Console.ReadLine();
}
}
}
C++ Project
#include <iostream>
// Expose function
extern "C" __declspec(dllexport) void HelloWorld();
void HelloWorld()
{
std::cout << "Hello World" << std::endl;
}
The problem now is:
When I build both projects there are no errors.
When running the project, the C++ DLL cannot be found. This, I found out is because of the incorrect build pathes.
Whereas the C++ correctly resolves the environment variables I set, the C# project doesn't. It creates folders named as the variable name itself, instead of building into the resolved path name.
The build for the C++ file resolves correctly the output path
Output C++ build
The build for the C# project doesn't
Output C# build
I just cannot figure out how to resolve this issue, but I am also fairly new to this. Can you help me out?
Regards
Sebastian

Debugging a failure to access libleveldb with C# on OSX

A C# project I'm interested in uses leveldb via a PInvoke 'wrapper' that works fine on both Windows and Linux but throws the following error on OSX.
src/tcmalloc.cc:331] Attempt to free invalid pointer 0x7f83cb954a00
A minimal example to reproduce the error with is
using System;
using System.Runtime.InteropServices;
namespace leveldb_test2
{
internal static class Native {
[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr leveldb_options_create();
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("before");
IntPtr handle = Native.leveldb_options_create();
Console.WriteLine("after");
}
}
}
What I've tried
Manually building leveldb
Leveldb was initially installed via homebrew. Not knowing if they do anything magical, I also did a manual build which gave me the same error.
Verifying the method signature
The method signature used with DllImport seems ok, as it's identical to what's used in a wrapper project I found: leveldb-sharp (fwiw; I tried using that project, same error)
Note that original export from leveldb is
extern leveldb_options_t* leveldb_options_create();
I also created my own dll with the same signature and that worked fine. This also confirmed for me that I can correctly enforce which exact library file is being loaded to avoid it using some broken version somewhere else on my system.
Verifying library exports
I dumped the exports using nm to make sure no name mangling is going on, seems fine.
I can also access the library successfully with ctypes in Python.
Try older versions of leveldb
I tried all older versions up to 1.15 from September 2014. All give the same error.
What's next
Ideally, I'd like to be able to debug the native side. However, unlike with Visual Studio on Windows, the OSX Visual Studio Community Edition doesn't have the "Enable native code debugging" option described here. So my question is
How would I go about debugging the native side while initiated from C#?
I believe the latter part of this question is important, because as said before it works fine when initiated from Python. Any tips/hints/help are much appreciated!
PS: a bonus would be the solution to getting the wrapper to work.

C++ dll within c#

I have C++ DLL as below
#include "stdafx.h"
extern "C" __declspec(dllexport)double Add(double a, double b);
extern double Add(double a, double b)
{
return a + b;
}
n here m trying to link this DLL with my C# app
using System.Text;
using System.Runtime.InteropServices;
namespace test
{
class Program
{
[DllImport("DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern double Add(double a, double b);
static void Main(string[] args)
{
Console.WriteLine(Add(1.0, 3.0)); // error here
Console.ReadLine();
}
}
}
m getting error:
"Unable to load DLL 'DLL.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)"
please help me out ...how can i link c++ dll with c# ?
The calling convention determines how function parameters are placed on the stack prior to a function invocation, and how they are removed (caller vs. callee) when the function returns. You can find out much more about this in about a million StackOverflow questions, or goto here and read up a little.
Regarding placement of the DLL within reach of the C# (aka .NET) application you're writing, I'm afraid I cannot comment on that except to say general DLL's must be in your lib-search path (PATH in Windows) the current directory, or the kernel's home directory (generally c:\windows\system32. Do NOT copy files to system32, btw. just setup your application to "run from" the directory where your DLL is residing and you should be fine. There are exceptions to this, and configuration settings that can radically alter this, but were I you i'd stick with simple for now. Complex can always come later.
You will either need to plant the dll in the same location as the C# exe or pack the dll inside the exe. The first option is simple enough. For the second option, check out Embedding DLL's into .exe in in Visual C# 2010
you got this error because DLL.dll wasn't in your Debug/Release Folder,
as far as i know visual studio doesn't know how to copy those files to your output folder manualy.
add the dll file to your C# solution
and then on files properties set build action to content
and set copy to output directory to copy if newer this will automate the copying

P/Invoke C# to C++

I'm trying to learn how to run C# and C++ code together using Mono on RedHat. I'm fooling around in order to learn how to get the two to interoperate together in order to be a bit more educated when I work on a larger project.
I've got a problem that I'm making a P/Invoke call from the C# to my C++ code and an exception is being thrown. Using Mono, I can get the C++ code to call the C# code no problem.
My C++ method that I want the C# to call is as follows.
extern "C"{
void Foobar(){
printf("Hooray!");
}
}
My C# code that I have uses the following P/Invoke lines.
[DllImport ("__Internal", EntryPoint="Foobar")]
static extern void Foobar();
In my C# program, I call
Foobar();
further down in a function. The exception I catch is an EntryPointNotFound exception. I'm probably overlooking something silly.
I've used http://www.mono-project.com/Embedding_Mono as instructions regarding how to do this.
Any suggestions appreciated. Thanks,
mj
Are you using embedding (that is, you build your own executable that inits the mono runtime)? In that case the possibilitites are usually two:
You have a typo
The compiler/linker removed during optimization the function from your binary
To check for either, run:
nm your_program |grep Foobar
and see if a symbol with that name is present in the executable your_program.
If you see a mangled name it means extern "C" was not applied correctly in your code.
If you're not using embedding, you need to use the dynamic library name and not __Internal in DllImport (and check for typos and the above linker optimization issue as well).
Why "__Internal"? That's for P/Invoking symbols in the Mono runtime or the program embedding the Mono runtime. How is your C++ code being compiled and linked?
I saw this exact problem, objdump -T and objdump -t differed on the host binary.
It was missing the 'D' flag, which means add a -rdynamic to the gcc linker.

BadImageFormatException: PInvoke ImportDll with hdf5dll.dll

Ok, I have the HDF5 library downloaded from the official site, and I have a few DLLs, including hdf5dll.dll, and hdf5_hldll.dll.
I have what I think to be some wrappers around the native calls, in my classes H5, H5LT, H5F, and H5T. Example from H5.cs:
namespace HDF5
{
using hid_t = System.Int32;
using herr_t = System.Int32;
using hsize_t = System.UInt64;
using size_t = System.UInt32;
// hbool_t is 0:false, +:true
using hbool_t = System.UInt32;
// htri_t is 0:false, +:true, -:failure
using htri_t = System.Int32;
public class H5
{
const CharSet StringMarshallingType = CharSet.Ansi;
const string DLLNAME = "hdf5dll.dll";
///* Functions in H5.c */
//H5_DLL herr_t H5open(void);
[DllImport(DLLNAME,
CharSet = StringMarshallingType)]
public static extern herr_t H5open();
And in Program.cs, I use H5.H5open();, but I get a BadImageFormatException. Do I need a different DLL? Does the method signature look wrong?
I'd like to, as the next step, get this in C#: http://www.hdfgroup.org/HDF5/Tutor/h5lite.html .
OS: Windows 7 64 bit
Environment: Visual Studio 2008 Professional
Update: I don't know if this will be related, and I don't remember if my environment is VS2008 SP1, but this question may hold a key to solving the mystery. I am as of now trying to repeat the scenario on 32 bit VS 2010 at home.
That happens when you're trying to run P/Invoke operations on a dll meant for x86 architecture from within an x64 process or vice versa. I'd check all of that and if they're out of sync, consider targeting the processor that HDF5 targets with your application, or checking if a processor-specific version is available.
Looking at the documentation from here, the function prototype is:
herr_t H5open(void);
And also the DLLNAME is disallowed, you must explicitly specify the dll name - no questions asked.
The proper signature is:
[DllImport("hdf5dll.dll")]public static extern herr_t H5open();
Make sure you have the type herr_t defined...
Let the runtime take care of the marshalling for you....
Also make sure, the DLL is present in the same path as where the compiled .EXE (your code) is generated.
Edit: Thanks to the OP for pointing out my blooper....
On x64 operatingsystems .net programs usually run in x64 mode.
Just set your target processor architecture to x86 and try again.
Just in Visual studio open your "Solution Configuration"-Manager and add a new target Platform.

Categories