I need to call a function (c/c++) that uses the multi-byte character set from C#. But I don't know how to marshall it as multi-byte. Does anyone know how to convert the result to a string?
C#:
[DllImport("essentials.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern System.IntPtr GetFiles(string filedir, string path);
[STAThread]
static void Main()
{
string filedir = #"C:\Users\Ruben\Documents\School\*";
string path = #"C:\Users\Ruben\Documents\School\";
System.IntPtr pointer = GetFiles(filedir, path);
string files = Marshal.PtrToStringAnsi(pointer); // nothing
}
C++
extern "C"
{
__declspec(dllexport) char* GetFiles(char* filedir, char* path)
{
string filedir2 = filedir;
string path2 = path;
string files = GetFiles2(filedir2, path2);
char* Rfiles = new char[files.length() + 1];
strcpy_s(Rfiles, files.length() + 1, files.c_str());
return Rfiles;
}
}
Information on MSDN : Default Marshaling for Strings
You need the marshal the C# string first, try this:
[DllImport("essentials.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern System.IntPtr GetFiles
([MarshalAs(UnmanagedType.AnsiBStr)]string filedir,
[MarshalAs(UnmanagedType.AnsiBStr)]string path);
Related
I tried to call a dll that is coded in delphi in C#. (delphi 11 64bit)
I believe there will be some problems.
Therefore, I tried some example in these website:
Calling a Delphi method in a dll from c#
Calling a Delphi DLL from a C# .NET application
But there is no response, and the applicaiton closes.
These are my codes:
example 1
delphi
function DBConnect1(inputStr,connStr:PWideChar):PWideChar;stdcall;
begin
try
result:=PWideChar('Hello from Delphi!');
except
result:=PWideChar('Exception');
end;
end;
C#
[DllImport("Project1.dll",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string DBConnect1(string inputString, string connectionString);
string inputString = "Parker";
string connectionString = "MyComputer";
string dbStrObj1 = DBConnect1(inputString, connectionString);
MessageBox.Show(dbStrObj1);
example 2
delphi
function Test1(sFD,sVD,sINI,sCh,sSD: string): PWideChar;stdcall;
var
tempStr:string;
str:WideString;
begin
tempStr:=sFD+sVD+sINI+sCh+sSD;
try
result:= PWideChar(tempStr);
except
str:='Error';
result:=PWideChar(str);
end;
result:=PWideChar(str);
end;
C#
[DllImport("Project1.dll", EntryPoint = "LoginLic", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string LoginLic(string s1, string s2, string s3, string s4, string s5);
string strId = "PCMS";
string strVersion = "AD19";
string strIni = #"";
string strCheck = "0";
string strSubDate = null;
string aa;
aa = UseDll.LoginLic(strId, strVersion, strIni, strCheck, strSubDate);
You have to marshall function parameters also. Refer this link for passing string with Marshalling https://learn.microsoft.com/en-us/dotnet/framework/interop/default-marshalling-for-strings
[DllImport("Project1.dll",CallingConvention = CallingConvention.StdCall,CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string DBConnect1(MarshalAs(UnmanagedType.LPWStr)]string inputString, MarshalAs(UnmanagedType.LPWStr)] string connectionString);
According the function description in "http://msdn.microsoft.com/en-us/library/cc196998%28v=VS.85%29.aspx", I wrote the following code to try to get IE protected cookies:
public static string GetProtectedModeCookie(string lpszURL, string lpszCookieName, uint dwFlags)
{
var size = 255;
var sb = new System.Text.StringBuilder(size);
var acturalSize = sb.Capacity;
var code = IEGetProtectedModeCookie(lpszURL, lpszCookieName, sb, ref acturalSize, dwFlags);
if ((code & 0x80000000) > 0) return string.Empty;
if (acturalSize > size)
{
sb.EnsureCapacity(acturalSize);
IEGetProtectedModeCookie(lpszURL, lpszCookieName, sb, ref acturalSize, dwFlags);
}
return sb.ToString();
}
[DllImport("ieframe.dll", SetLastError = true)]
public static extern uint IEGetProtectedModeCookie(string lpszURL, string lpszCookieName, System.Text.StringBuilder pszCookieData, ref int pcchCookieData, int dwFlags);
to test this function:
var cookies = GetProtectedModeCookie("http://bbs.pcbeta.com/", null, 0);
But the api IEGetProtectedModeCookie always return 0x80070057 which indicates that one or more arguments are invalid.
I was confused, after a lot of try finally failed, only get this result. Can anybody help me?
IEGetProtectedModeCookie will return E_INVALIDARG if it thinks that the URL isn't meant to open in Protected Mode. It determines this using the IEIsProtectedModeURL API. So if you've put that URL in the Trusted Zone or whatnot, then you'll hit this error. The underlying InternetGetCookie API will return E_INVALIDARG if you fail to pass a URL or fail to pass a pointer to an integer for the size of the buffer.
Also note that the IEGetProtectedModeCookie API will not work from a high integrity (e.g. Admin) process; it will return ERROR_INVALID_ACCESS (0x8000000C).
Here's the code I use:
[DllImport("ieframe.dll", CharSet = CharSet.Unicode, EntryPoint = "IEGetProtectedModeCookie", SetLastError = true)]
public static extern int IEGetProtectedModeCookie(String url, String cookieName, StringBuilder cookieData, ref int size, uint flag);
private void GetCookie_Click(object sender, EventArgs e)
{
int iSize = 4096;
StringBuilder sbValue = new StringBuilder(iSize);
int hResult = IEAPI.IEGetProtectedModeCookie("http://www.google.com", "PREF", sbValue, ref iSize, 0);
if (hResult == 0)
{
MessageBox.Show(sbValue.ToString());
}
else
{
MessageBox.Show("Failed to get cookie. HRESULT=0x" + hResult.ToString("x") + "\nLast Win32Error=" + Marshal.GetLastWin32Error().ToString(), "Failed");
}
}
Charset parameter must be exist in DllImport attribute. Change the API declartion to follow will works well:
[DllImport("ieframe.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint IEGetProtectedModeCookie(string lpszURL, string lpszCookieName, System.Text.StringBuilder pszCookieData, ref int pcchCookieData, uint dwFlags);
If Charset parameter missed, this API will always return 0x80070057 which indicates one or more arguments are invalid.
I am trying to upgrade to the MS .NET Framework 4 and I am encountering an error with a dllimport function (see below). When the code reaches the dllimport, the program just exits with error code 0x80000003. I have tried changing it so the input and output are StringBuilder and I also tried setting the charset by both changing the charset (to either unicode or ansi) and setting a EntryPoint (either PathGetArgsA or PathGetArgsW). This code works fine in v3.5 but not in v4.
[DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern string PathGetArgs([In] string path);
[DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern void PathRemoveArgs([In, Out] StringBuilder path);
public static bool ExtractArguments(string cmdLine, out string filePath, out string fileArgs)
{
StringBuilder strCmdLine = new StringBuilder(cmdLine.ToLower().Trim());
filePath = fileArgs = "";
if (strCmdLine.Length <= 0)
throw new ArgumentNullException("cmdLine");
fileArgs = string.Copy(PathGetArgs(strCmdLine.ToString())); // Error occurs here
PathRemoveArgs(strCmdLine);
filePath = string.Copy(strCmdLine.ToString());
if (!string.IsNullOrEmpty(filePath))
if (Utils.FileExists(filePath))
return true;
return false;
}
Thanks!
I'm not sure what the native dll retuns in the PathGetArgs however, the Marshal Class could help.
[DllImport("Shlwapi.dll")]
public static extern IntPtr PathGetArgs([In] string path);
fileArgs = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(PathGetArgs(strCmdLine.ToString()));
1.)
How do Load, Edit and Save binary Hive files for registry from C#?
I found this Win32 api.
http://msdn.microsoft.com/en-us/library/ee210770%28VS.85%29.aspx
This guy shared the code to dump the content of binary Hive files to text.
http://www.codeproject.com/KB/recipes/RegistryDumper.aspx
2.)
In addition to manipulating the Hive files, I also search for a method to load the Hive file into registry at runtime using C#
(similar to the Load Hive and Unload Hive commands on the File many in regedit)
/Thanks
Have you looked at the Registry and RegistryKey classes in Microsoft.Win32?
http://msdn.microsoft.com/en-us/library/microsoft.win32.aspx
It sounds like you may need to create your own representation to read the hive file and either queue up or immediately make the corresponding registry changes. Likewise you would need to write your own converter back to disk.
The article below explains how to analyze the registry file without using WinAPI (advapi32.dll). In this particular case the guy is using Mono:
http://volatile-minds.blogspot.com/2011/01/analyzing-windows-nt-registry-without.html
using (FileStream fs = File.OpenRead (path)) {
var data = new byte[checked((int)fs.Length)];
int i = 0;
int read;
using (var ms = new MemoryStream (checked((int)fs.Length))) {
while ((read = fs.Read (data, 0, data.Length)) > 0) {
ms.Write (data, 0, read);
i += read;
}
byte[] hive = ms.ToArray ();
char[] cList = new char[fs.Length];
i = 0;
foreach (byte b in hive)
cList[i++] = (char)b;
string d = new string (cList);
int all = 0;
foreach (Match mx in lf.Matches (d)) { //you can change out the regex you want here.
byte[] bb = new byte[mx.Value.Length];
char[] cb = new char[mx.Value.Length];
for (int k = 0; k < mx.Value.Length; k++) {
bb[k] = (byte)mx.Value[k];
cb[k] = (char)bb[k];
}
all++;
//Console.WriteLine (new string (cb));
}
Console.WriteLine (all.ToString ());
all = 0;
}
}
This is 9 years old, but I figured this could help someone else. I wrote this class that allows you to do something like this:
Hive.AcquirePrivileges() // Acquires the privileges necessary for loading the hive
Hive myregistryhive = Hive.LoadFromFile("hivepathhere") // Loads the hive
// use myregistryhive.RootKey (a RegistryKey), read and/or write to it and its sub keys
myregistryhive.SaveAndUnload() // Unloads the hive
Hive.ReturnPrivileges() // De-elevate back to normal privileges.
The code for the class:
class Hive
{
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RegLoadKey(IntPtr hKey, string lpSubKey, string lpFile);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RegSaveKey(IntPtr hKey, string lpFile, uint securityAttrPtr = 0);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RegUnLoadKey(IntPtr hKey, string lpSubKey);
[DllImport("ntdll.dll", SetLastError = true)]
static extern IntPtr RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue);
[DllImport("advapi32.dll")]
static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref UInt64 lpLuid);
[DllImport("advapi32.dll")]
static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpName, ref UInt64 lpLuid);
private RegistryKey parentKey;
private string name;
private string originalPath;
public RegistryKey RootKey;
private Hive() { }
public static Hive LoadFromFile(string Path)
{
Hive result = new Hive();
result.parentKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
result.name = Guid.NewGuid().ToString();
result.originalPath = Path;
IntPtr parentHandle = result.parentKey.Handle.DangerousGetHandle();
RegLoadKey(parentHandle, result.name, Path);
//Console.WriteLine(Marshal.GetLastWin32Error());
result.RootKey = result.parentKey.OpenSubKey(result.name, true);
return result;
}
public static void AcquirePrivileges()
{
ulong luid = 0;
bool throwaway;
LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid);
RtlAdjustPrivilege((int)luid, true, false, out throwaway);
LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid);
RtlAdjustPrivilege((int)luid, true, false, out throwaway);
}
public static void ReturnPrivileges()
{
ulong luid = 0;
bool throwaway;
LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid);
RtlAdjustPrivilege((int)luid, false, false, out throwaway);
LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid);
RtlAdjustPrivilege((int)luid, false, false, out throwaway);
}
public void SaveAndUnload()
{
RootKey.Close();
RegUnLoadKey(parentKey.Handle.DangerousGetHandle(), name);
parentKey.Close();
}
}
Edit: Note that this requires administrator privileges.
please see: https://github.com/brandonprry/volatile_reader
It reads offline hives in C# with a GTK interface. No write support yet though.
How can I determine if a remote drive has enough space for me to upload a given file using C# in .Net?
There are two possible solutions.
Call the Win32 function GetDiskFreeSpaceEx. Here is a sample program:
internal static class Win32
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool GetDiskFreeSpaceEx(string drive, out long freeBytesForUser, out long totalBytes, out long freeBytes);
}
class Program
{
static void Main(string[] args)
{
long freeBytesForUser;
long totalBytes;
long freeBytes;
if (Win32.GetDiskFreeSpaceEx(#"\\prime\cargohold", out freeBytesForUser, out totalBytes, out freeBytes)) {
Console.WriteLine(freeBytesForUser);
Console.WriteLine(totalBytes);
Console.WriteLine(freeBytes);
}
}
}
Use the system management interface. There is another answer in this post which describes this. This method is really designed for use in scripting languages such as PowerShell. It performs a lot of fluff just to get the right object. Ultimately, I suspect, this method boils down to calling GetDiskFreeSpaceEx.
Anybody doing any serious Windows development in C# will probably end up calling many Win32 functions. The .NET framework just doesn't cover 100% of the Win32 API. Any large program will quickly uncover gaps in the .NET libraries that are only available through the Win32 API. I would get hold of one of the Win32 wrappers for .NET and include this in your project. This will give you instant access to just about every Win32 API.
Use WMI
using System.Management;
// Get all the network drives (drivetype=4)
SelectQuery query = new SelectQuery("select Name, VolumeName, FreeSpace from win32_logicaldisk where drivetype=4");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject drive in searcher.Get())
{
string Name = (string)drive["Name"];
string VolumeName = (string)drive["VolumeName"];
UInt64 freeSpace = (UInt64)drive["FreeSpace"];
}
based on (stolen from) http://www.dreamincode.net/code/snippet1576.htm
Are you talking about mapping a network share to a logical drive on you computer?
If so you can use DriveInfo.
DriveInfo info = new DriveInfo("X:");
info.AvailableFreeSpace;
DriveInfo only works with logical drives so if you are just using the full share (UNC) name I don't think the above code will work.
I'm not sure if GetDiskFreeSpaceEx works on UNC shares, but if it does use that, otherwise here is how to mount a UNC share to a logal drive:
EDIT GetDiskFreeSpaceEx does work on UNC shares, use that...however, this code was too much effort to just delete, and is handy if you ever want to mount a UNC share as a local drive in your code.
public class DriveWrapper
{
[StructLayout(LayoutKind.Sequential)]
public struct NETRESOURCEA
{
public int dwScope;
public int dwType;
public int dwDisplayType;
public int dwUsage;
[MarshalAs(UnmanagedType.LPStr)]
public string lpLocalName;
[MarshalAs(UnmanagedType.LPStr)]
public string lpRemoteName;
[MarshalAs(UnmanagedType.LPStr)]
public string lpComment;
[MarshalAs(UnmanagedType.LPStr)]
public string lpProvider;
public override String ToString()
{
String str = "LocalName: " + lpLocalName + " RemoteName: " + lpRemoteName
+ " Comment: " + lpComment + " lpProvider: " + lpProvider;
return (str);
}
}
[DllImport("mpr.dll")]
public static extern int WNetAddConnection2A(
[MarshalAs(UnmanagedType.LPArray)] NETRESOURCEA[] lpNetResource,
[MarshalAs(UnmanagedType.LPStr)] string lpPassword,
[MarshalAs(UnmanagedType.LPStr)] string UserName,
int dwFlags);
[DllImport("mpr.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private static extern int WNetCancelConnection2A(
[MarshalAs(UnmanagedType.LPStr)]
string lpName,
int dwFlags,
int fForce
);
public int GetDriveSpace(string shareName, string userName, string password)
{
NETRESOURCEA[] n = new NETRESOURCEA[1];
n[0] = new NETRESOURCEA();
n[0].dwScope = 0;
n[0].dwType = 0;
n[0].dwDisplayType = 0;
n[0].dwUsage = 0;
n[0].dwType = 1;
n[0].lpLocalName = "x:";
n[0].lpRemoteName = shareName;
n[0].lpProvider = null;
int res = WNetAddConnection2A(n, userName, password, 1);
DriveInfo info = new DriveInfo("x:");
int space = info.AvailableFreeSpace;
int err = 0;
err = WNetCancelConnection2A("x:", 0, 1);
return space;
}
}