Windows kernel32 functions in Mono on Linux - c#

I got this awesomely simple ini class that I downloaded from somewhere a while ago, but now that I'm using mono I'm running into the issue that it's importing stuff from kernel32
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section,
string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section,
string key, string def, StringBuilder retVal,
int size, string filePath);
Which on mono (in linux) gives the error DLLNotFoundException: kernel32
Is there any way to get this to work with mono? Maybe embed the whole thing into the assembly at compile time (if that even makes sense at all, I wouldn't know). Or will I have to create/find an ini class that doesn't use WinAPI? (Nini springs to mind).
I'd really like it if WinAPI stuff could work with Mono, any thoughts?

Mono supports C#'s P/Invoke, which is what's required for running Win32 API functions. (As long as you're running Mono on Windows--the fact that it can't find "kernel32" causes me to suspect you're not.)
The site pinvoke.net collects the necessary DllImport signatures for most of the Win32 API.
Here's what it has to say about GetPrivateProfileString.
This code worked for me using Mono 2.10.8 on Windows 7:
using System;
using System.Text;
public class MainClass
{
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section,
string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section,
string key, string def, StringBuilder retVal,
int size, string filePath);
static void Main()
{
StringBuilder asdf = new StringBuilder();
GetPrivateProfileString("global","test","",asdf,100,#"c:\example\test.ini");
Console.WriteLine(asdf.ToString());
}
}

You'll need to rewrite the functionality of those functions in native .NET to use them on Mono/Linux (unless you can convince Mono and Wine to play nicely).
If the INI files are controlled, then you may get away with simple file/string manipulation, but then you may be better off moving to something a bit more cross platform anyway.

Change [DllImport("kernel32")] into [DllImport("kernel32.dll")]
Everything will start working like supposed to.

Related

How do I get the name of the current executable in C#? (.NET 5 edition)

How do I get the name the executable was invoked as (equivalent to C's argv[0])? I actually need to handle somebody renaming the executable and stuff like that.
There's a famous question with lots of answers that don't work. Answers tried:
System.AppDomain.CurrentDomain.FriendlyName
returns the name it was compiled as
System.Diagnostics.Process.GetCurrentProcess().ProcessName
strips extension (ever rename a .exe to a .com?), also sees through symbolic links
Environment.GetCommandLineArgs()[0]
It returns a name ending in .dll, clearly an error.
Assembly.GetEntryAssembly().Location
Returns null
System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName
Returns a .dll name again.
The documentation for .net 5.0 says Environment.GetCommandLineArguments()[0] works; however it doesn't actually work. It somehow sees through symbolic links and returns the real executable name.
What I'm trying to do is link all of our stuff into a single multi-call binary so I can use the .net 5 framework reducer on the resulting binary so I don't have to ship about 30MB of .net 5 framework we're not using. I really don't want to do a platform ladder and P/Invoke a bunch of stuff unless I have to.
I'm after argv[0] directly, not the running process executable name. In the case of symbolic links, these differ.
Came across this with .NET 6, where Process.GetCurrentProcess().MainModule?.FileName seems to be working fine now, and there's also Environment.ProcessPath.
If targeting Windows only, it might be safer (more predictable) to use interop. Below are some options, including the native GetModuleFileName and GetCommandLine:
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
Console.WriteLine("Process.GetCurrentProcess().MainModule?.FileName");
Console.WriteLine(Process.GetCurrentProcess().MainModule?.FileName);
Console.WriteLine();
Console.WriteLine("Assembly.GetExecutingAssembly().Location");
Console.WriteLine(Assembly.GetExecutingAssembly().Location);
Console.WriteLine();
Console.WriteLine("Environment.ProcessPath");
Console.WriteLine(Environment.ProcessPath);
Console.WriteLine();
Console.WriteLine("Environment.CommandLine");
Console.WriteLine(Environment.CommandLine);
Console.WriteLine();
Console.WriteLine("Environment.GetCommandLineArgs()[0]");
Console.WriteLine(Environment.GetCommandLineArgs()[0]);
Console.WriteLine();
Console.WriteLine("Win32.GetProcessPath()");
Console.WriteLine(Win32.GetProcessPath());
Console.WriteLine();
Console.WriteLine("Win32.GetProcessCommandLine()");
Console.WriteLine(Win32.GetProcessCommandLine());
Console.WriteLine();
public static class Win32
{
private const int MAX_PATH = 260;
private const int INSUFFICIENT_BUFFER = 0x007A;
private const int MAX_UNICODESTRING_LEN = short.MaxValue;
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr GetCommandLine();
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[PreserveSig]
[return: MarshalAs(UnmanagedType.U4)]
private static extern int GetModuleFileName(
IntPtr hModule, StringBuilder lpFilename, [MarshalAs(UnmanagedType.U4)] int nSize);
public static string GetProcessCommandLine()
{
return Marshal.PtrToStringUni(GetCommandLine()) ??
throw new Win32Exception(nameof(GetCommandLine));
}
public static string GetProcessPath()
{
var buffer = new StringBuilder(MAX_PATH);
while (true)
{
int size = GetModuleFileName(IntPtr.Zero, buffer, buffer.Capacity);
if (size == 0)
{
throw new Win32Exception();
}
if (size == buffer.Capacity)
{
// double the buffer size and try again.
buffer.EnsureCapacity(buffer.Capacity * 2);
continue;
}
return Path.GetFullPath(buffer.ToString());
}
}
}
The output when running via dotnet run:
Process.GetCurrentProcess().MainModule?.FileName
C:\temp\ProcessPath\bin\Debug\net6.0\ProcessPath.exe
Assembly.GetExecutingAssembly().Location
C:\temp\ProcessPath\bin\Debug\net6.0\ProcessPath.dll
Environment.ProcessPath
C:\temp\ProcessPath\bin\Debug\net6.0\ProcessPath.exe
Environment.CommandLine
C:\temp\ProcessPath\bin\Debug\net6.0\ProcessPath.dll
Environment.GetCommandLineArgs()[0]
C:\temp\ProcessPath\bin\Debug\net6.0\ProcessPath.dll
Win32.GetProcessPath()
C:\temp\ProcessPath\bin\Debug\net6.0\ProcessPath.exe
Win32.GetProcessCommandLine()
"C:\temp\ProcessPath\bin\Debug\net6.0\ProcessPath.exe"
Oh, and for a Windows Forms application, there always has been Application.ExecutablePath.
Updated, running it on Ubuntu 22.04 with .NET 6.0.2 (with Win32 interop removed), either via dotnet run or directly as ./ProcessPath:
Process.GetCurrentProcess().MainModule?.FileName
/home/noseratio/ProcessPath/bin/Debug/net6.0/ProcessPath
Assembly.GetExecutingAssembly().Location
/home/noseratio/ProcessPath/bin/Debug/net6.0/ProcessPath.dll
Environment.ProcessPath
/home/noseratio/ProcessPath/bin/Debug/net6.0/ProcessPath
Environment.CommandLine
/home/noseratio/ProcessPath/bin/Debug/net6.0/ProcessPath.dll
Environment.GetCommandLineArgs()[0]
/home/noseratio/ProcessPath/bin/Debug/net6.0/ProcessPath.dll
After watching everything fail, it became necessary to P/Invoke stuff to make this work. While Process.GetCurrentProcess().MainModule?.FileName reliably returns the executable binary (at least when not running under the debugger), this does not provide the command invocation the binary was launched with.
On Windows, GetCommandLine() is P/Invokable and needs only some parsing to get the information. On *n?x, reading /proc/self/cmdline does the same job.
I built a library encapsulating this. https://github.com/joshudson/Emet/tree/master/MultiCall You can find binaries on nuget.org ready to go.
I should have self-answered a long time ago. Better late than never. Nobody seemed to care until now.
Using .NET Core 3.1, this worked for me to restart a currently running program.
string filename = Process.GetCurrentProcess().MainModule.FileName;
System.Diagnostics.Process.Start(filename);
// Closes the current process
Environment.Exit(0);

How to P/Invoke os_log?

What should the syntax be to call the MacOS' os_log from C# in a .NET Core console app?
Based on
https://developer.apple.com/documentation/os/os_log
and
How to use iOS OSLog with Xamarin?
and
https://opensource.apple.com/source/xnu/xnu-4903.221.2/libkern/os/log.h.auto.html
I was expecting something like this:
using System.Runtime.InteropServices;
namespace Foo
{
class Program
{
[DllImport("__Internal", EntryPoint = "os_log_create")]
private static extern IntPtr os_log_create(string subsystem, string category);
[DllImport("__Internal", EntryPoint = "os_log")]
private static extern void os_log(IntPtr log, string format, string message);
static void Main(string[] args)
{
IntPtr log = os_log_create("some.bundle.id", "SomeCategory");
os_log(log, "%s", "Test!");
}
}
}
However, when I try to run this on my Mac I get a System.DllNotFoundException that says Unable to load shared library '__Internal' or one of its dependencies... .
Any help with this issue or P/Invoke between C# and MacOS would be very helpful, thanks!
Macro os_log
In contrast to the os_log_create function, os_log is a macro, as already mentioned in the comments.
So if you would write in C:
os_log(log, "%{public}s", "Test!");
It would finally call a function named _os_log_impl, but the first parameter of that would be a pointer __dso_handle to which we don't have access from the managed side.
Possible Solution
But you don't have to do without the new logging system from Apple. One possibility is to create a dynamic library that provides a defined API that can easily be called from the managed C# code.
How to Create a Dynamic Library in Xcode
It is easy to create a dynamic library in Xcode:
choose in XCode <File/New Project>
choose Library template in the macOS section
use Type Dynamic
Minimal Example
A minimal .c example for our own Logging library might look like this:
#include <os/log.h>
extern void Log(os_log_t log, char *message) {
os_log(log, "%{public}s", message);
}
Calling from .Net
I took your source and only slightly modified it:
using System;
using System.Runtime.InteropServices;
namespace Foo
{
class Program
{
[DllImport("System", EntryPoint = "os_log_create")]
private static extern IntPtr os_log_create(string subsystem, string category);
[DllImport("Logging", EntryPoint = "Log")]
private static extern void Log(IntPtr log, string msg);
static void Main(string[] args)
{
IntPtr log = os_log_create("some.bundle.id", "SomeCategory");
Log(log, "Test!");
}
}
}
The dynamic library created with Xcode has the name Logging. Our in C created logging function is named Log here.
Of course you can design the API as comfortable as you want, this should be a minimal example that is as close to the question as possible.
Output in Console Utility
The output in the Console utility (if you filter for some.bundle.id) would look like this:

How to hack Win32 API to get character limit to 32K

I want to use prefix \\\?\ as stated in this msdn BCL Team Blog, Long Paths in .NET, Part 2 of 3: Long Path Workarounds [Kim Hamilton]
Even after going through it again and again, I couldn't figure out how to actually use this feature, wondering if anyone can tell me simplest way to use it and how.
Note: I want to use it for creating a directory
You have to use Win32 functions and P/Invoke to achieve this. Use the Unicode version of the API.
Here's what you're looking for:
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CreateDirectory(string lpPathName, IntPtr lpSecurityAttributes);
public static void CreateDir(string dirPath)
{
if (!CreateDirectory(#"\\?\" + dirPath, IntPtr.Zero))
{
throw new IOException("Could not create dir");
}
}
CreateDirectory method
More information about how naming works in Windows

Use c++ libraries at C#

I'm working myself on a project, because performance is important i want to write some part of the code on c++ and want to call c++ codes from C#. I will write c++ library and want to write a C# wrapper, how can i do it? Have you any suggestion where i should begin to study? [any website, any link, any book]
Thanks !
Example:
Windows:
[DllImport("User32.dll")]
public static extern void SetWindowText(int h, String s);
Linux:
[System.Runtime.InteropServices.DllImport("/root/Desktop/q3noclient/libnoclient.so")]
static extern int DisconnectClient (string strServerIP, int iServerPort, string strClientIP, int iClientPort);
C#:
[DllImport("Comdlg32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool GetSaveFileName(ref OPENFILENAME lpofn);
VB.NET:
<DllImport("Comdlg32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function GetSaveFileName(ByRef lpofn As OPENFILENAME) As Boolean
End Function
You need to either make C style imports for P/Invoke to use, like
void World_Hello(World* self) { self->Hello(); }
or investigate CallingConvention.ThisCall. Thanks to name mangling, if you want to be portable to other OSes the former is a better choice.
Take a look at C++/CLI. I would recommend C++/CLI in Action by Nishant Sivakumar. Also, check this link out: http://msdn.microsoft.com/en-us/magazine/cc163855.aspx#S7

Why can't I get GetPrivateProfileString to work via pinvoke?

Running a c# console app I wrote on 64 bit Vista. Here's the code:
class Class1
{
static void Main(string[] args)
{
Debug.Assert(File.Exists(#"c:\test.ini"));
StringBuilder sb = new StringBuilder(500);
uint res = GetPrivateProfileString("AppName", "KeyName", "", sb, sb.Capacity, #"c:\test.ini");
Console.WriteLine(sb.ToString());
}
[DllImport("kernel32.dll")]
static extern uint GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName);
}
I'm sure I'll get a big "DUH!" for an answer, but I'm not seeing why this fails to work. Other than the Debug.Assert, this code was cut from the c# sample at this page
This one has been busting my chops all day, too. I think I found a work-around, which I really don't want to have to deal with: insert a blank line before the first section header in your .ini file. Now run your app and see if you don't start seeing the values you were expecting.
Considering this bug has apparently been around for years, I'm surprised MS hasn't fixed it by now. But then, .ini files were supposed to have gone away years ago. Which of course is funny, because there are a lot of places MS uses .ini file (e.g, desktop.ini). But I think MS is aware of the bug, because I notice my desktop.ini files include a leading blank line. Hmmm...
The main thing I see is that you should be passing in an uint for nSize, as well as the return value. This is because the return and nSize parameters of GetPrivateProfileString are DWORD values, which are unsigned 32bit integers.
I personally have used the syntax on PInvoke.net:
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
static extern uint GetPrivateProfileString(
string lpAppName,
string lpKeyName,
string lpDefault,
StringBuilder lpReturnedString,
uint nSize,
string lpFileName);
In addition, you'll need to put the full path to the file in place, unless the file is located in the Windows directory. From the docs:
If this parameter does not contain a full path to the file, the system searches for the file in the Windows directory.
According to pinvoke.net, nSize should be a UINT. Also they are using an absolute path in their example.
Other than those differences, I can't see anything else.
Providing it's throwing a invalid format exception, try setting target platform to x86 to solve the problem.
Usage example from pinvoke.net is
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
static extern uint GetPrivateProfileString(
string lpAppName,
string lpKeyName,
string lpDefault,
StringBuilder lpReturnedString,
uint nSize,
string lpFileName);
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder(500);
uint res = GetPrivateProfileString("AppName", "KeyName", "", sb, (uint)sb.Capacity, #"c:\test.ini");
Console.WriteLine(sb.ToString());
}
If no path is specified, GetPrivateProfileString will look for Test.ini in the Windows directory.
Old APIs like GetPrivateProfileString don't handle Unicode well (even though there's a GetPrivateProfileStringW function). If Test.ini contains a UTF header or Unicode characters, that might be enough to prevent GetPrivateProfileString from working correctly.
Also, Vista's UAC can make handling files that are in "special" places tricky (C:\, C:\Windows, C:\Program Files, etc.). Try putting Test.ini in a folder rather than in the root of the C: drive, or turn off UAC. There's a thread on CodeProject that discusses GetPrivateProfileString failing silently when trying to read an .ini from a folder controlled by UAC.
Maybe you should look into looking at an open source solution that will do exactly that without you worrying about p/Invoke's. The project targetted for .NET is called nini I am using this in a project I am working on and I recommend it.
Hope this helps,
Best regards,
Tom.
Can you verify the contents of your test.ini file? Given all of the steps that you've tried, I'm beginning to suspect that your data file is not formatted correctly (misspelling, etc.) In other words, GetPrivateProfileString may be "working", but just not finding your string. Based on the code that you posted, your test.ini file should look something like this:
[AppName]
KeyName=foo

Categories