c# code:
var nativeAutomation = new UIAutomationClient.CUIAutomation8();
nativeAutomation.AddPropertyChangedEventHandler(ele, UIA.TreeScope.TreeScope_Element, null, new handler(), pidarray);
the handler used in AddPropertyChangedEventHandler
class handler : UIAutomationClient.IUIAutomationPropertyChangedEventHandler
{
public void HandlePropertyChangedEvent(UIA.IUIAutomationElement src, int propertyId, object newValue)
{
UIA.IUIAutomationElement sourceElement = src as UIA.IUIAutomationElement;
Console.WriteLine(propertyId + ":" + newValue);
}
}
it works very well
but when i using c++:
#include "stdafx.h"
#include <Windows.h>
#include "test.h"
#include "cbt.h"
#include <UIAutomationClient.h>
#include <iostream>
#include <string>
#pragma comment(lib,"testDll.lib")
class A :public IUIAutomationPropertyChangedEventHandler {
ULONG m_ref;
public:
A() :m_ref(0)
{}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(const IID &id, void** p) override {
// return IUnknown::QueryInterface(id,p);
REFIID d = { 0x0000001b,0x0000,0x0000, {0xC0,00,00,00,00,00,00,0x46} };// IdentityUnmarshal.
if (id == d) {
return E_NOINTERFACE;
}
*p = this;
return S_OK;
//return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef() override {
++m_ref;
//return IUnknown::AddRef();
return m_ref;
}
virtual ULONG STDMETHODCALLTYPE Release() override {
// return IUnknown::Release();
--m_ref;
if (!m_ref)
delete this;
return m_ref;
}
virtual HRESULT STDMETHODCALLTYPE HandlePropertyChangedEvent(
/* [in] */ __RPC__in_opt IUIAutomationElement *sender,
/* [in] */ PROPERTYID propertyId,
/* [in] */ VARIANT newValue) {
printf("dsdsdsdsddsd\n");
return S_OK;
};
};
int main()
{
// cbt::Instance();
CoInitializeEx(NULL, COINIT_MULTITHREADED);
IUIAutomation* am = NULL;
HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation8), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation), (void**)&am);
if (S_OK != hr)
hr = CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation), (void**)&am);
A* a = new A;
std::string hx;
std::getline(std::cin, hx);
char* s = NULL;
HWND h = (HWND)strtol(hx.c_str(), &s, 16);
IUIAutomationElement* ele = NULL;
am->ElementFromHandle(h, &ele);
/*SAFEARRAY* sa = SafeArrayCreateVector(VT_I4, 0, 4);
LONG i = 0;
long pid = UIA_AutomationIdPropertyId;
SafeArrayPutElement(sa, &i, &pid);
i = 1;
pid = UIA_BoundingRectanglePropertyId;
SafeArrayPutElement(sa, &i, &pid);
i = 2;
pid = UIA_ClassNamePropertyId;
SafeArrayPutElement(sa, &i, &pid);
i = 3;
pid = UIA_NamePropertyId;
SafeArrayPutElement(sa, &i, &pid);
am->AddPropertyChangedEventHandler(ele, TreeScope_Element, NULL, p,sa );
SafeArrayDestroy(sa);*/
PROPERTYID *pids = new PROPERTYID[4];
pids[0] = UIA_AutomationIdPropertyId;
pids[1] = UIA_BoundingRectanglePropertyId;
pids[2] = UIA_ClassNamePropertyId;
pids[3] = UIA_NamePropertyId;
am->AddPropertyChangedEventHandlerNativeArray(ele, TreeScope_Element, NULL, a, pids, 4);
getchar();
CoUninitialize();
return 0;
}
so,it is very easy in c#.
but,with c++,i need to override Addref(), Release(), QueryInterface().
error occurs when call
am->AddPropertyChangedEventHandlerNativeArray(ele, TreeScope_Element, NULL, a, pids, 4);
looks like i should return a IMarshal object in the QueryInterface().
i think it needs a otherThread to loop events.
guys , how to code this IMarshal object?
ok,i got the answer at https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-howto-implement-event-handlers
my internet unstable these days.didn't see the page
Related
I am relatively new to C++, and have copied some code from a live stream to get started with embedding C# mono into my game engine.
I have created a C# script:
using System;
namespace Neutron {
public class Main {
public float FloatVar { get; set; }
public Main() {
Console.WriteLine("Main constructor");
}
public void PrintMessage() {
Console.WriteLine("Hello World from C#!");
}
public void PrintCustomMessage(string message) {
Console.WriteLine($"C# says: {message}");
}
}
}
and built it to
../Sandbox/Sandbox/bin/Debug/Sandbox.dll (which i have verified within the c++ program that I am embedding it into)
when i call mono_runtime_object_init passing the MonoObject* created from calling mono_object_new (I have verified that it doesnt return a nullptr)
The following error occurs:
* Assertion at object.c:116, condition `is_ok (error)' not met, function:mono_runtime_object_init, (null) assembly:/usr/lib/mono/4.5/mscorlib.dll type:TypeInitializationException member:(null)
Followed by a long stack trace.
Here are my relavent files:
ScriptingEngine.cpp (See the InitMono function towards the bottom)
//
// Created by aw1lt on 05/12/22.
//
#include "ScriptingEngine.h"
#include "Logger.h"
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <fstream>
namespace Neutron {
struct ScriptEngineData {
MonoDomain* RootDomain = nullptr;
MonoDomain* AppDomain = nullptr;
MonoAssembly* CoreAssembly = nullptr;
};
char* ReadBytes(const std::string& filepath, uint32_t* outSize) {
std::ifstream stream(filepath, std::ios::binary | std::ios::ate);
if (!stream) {
// Failed to open the file
return nullptr;
}
std::streampos end = stream.tellg();
stream.seekg(0, std::ios::beg);
uint32_t size = end - stream.tellg();
if (size == 0) {
// File is empty
return nullptr;
}
char* buffer = new char[size];
stream.read((char*)buffer, size);
stream.close();
*outSize = size;
return buffer;
}
MonoAssembly* LoadCSharpAssembly(const std::string& assemblyPath) {
uint32_t fileSize = 0;
char* fileData = ReadBytes(assemblyPath, &fileSize);
// NOTE: We can't use this image for anything other than loading the assembly because this image doesn't have a reference to the assembly
MonoImageOpenStatus status;
MonoImage* image = mono_image_open_from_data_full(fileData, fileSize, 1, &status, 0);
if (status != MONO_IMAGE_OK) {
const char* errorMessage = mono_image_strerror(status);
Logger::Crit(errorMessage);
return nullptr;
}
MonoAssembly* assembly = mono_assembly_load_from_full(image, assemblyPath.c_str(), &status, 0);
mono_image_close(image);
// Don't forget to free the file data
delete[] fileData;
return assembly;
}
void PrintAssemblyTypes(MonoAssembly* assembly) {
MonoImage* image = mono_assembly_get_image(assembly);
const MonoTableInfo* typeDefinitionsTable = mono_image_get_table_info(image, MONO_TABLE_TYPEDEF);
int32_t numTypes = mono_table_info_get_rows(typeDefinitionsTable);
for (int32_t i = 0; i < numTypes; i++) {
uint32_t cols[MONO_TYPEDEF_SIZE];
mono_metadata_decode_row(typeDefinitionsTable, i, cols, MONO_TYPEDEF_SIZE);
const char* nameSpace = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAMESPACE]);
const char* name = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAME]);
Logger::Log(std::string(nameSpace) + "." + name);
}
}
static ScriptEngineData* s_Data;
void ScriptingEngine::Init() {
s_Data = new ScriptEngineData();
InitMono();
}
void ScriptingEngine::Shutdown() {
delete s_Data;
}
void ScriptingEngine::InitMono(std::string path) {
MonoDomain* rootDomain = mono_jit_init("NeutronJITRuntime");
Logger::Assert(rootDomain != nullptr);
//system(("cat " + path).c_str());
// Store the root domain pointer
s_Data->RootDomain = rootDomain;
// Create an App Domain
s_Data->AppDomain = mono_domain_create_appdomain("NeutronScriptRuntime", nullptr);
mono_domain_set(s_Data->AppDomain, true);
s_Data->CoreAssembly = LoadCSharpAssembly(path);
Logger::Assert(s_Data->CoreAssembly != nullptr);
MonoImage* assemblyImage = mono_assembly_get_image(s_Data->CoreAssembly);
MonoClass* monoClass = mono_class_from_name(assemblyImage, "Neutron", "Main");
Logger::Assert(monoClass != nullptr);
PrintAssemblyTypes(s_Data->CoreAssembly);
MonoObject* instance = mono_object_new(s_Data->AppDomain, monoClass);
Logger::Assert(instance != nullptr);
mono_runtime_object_init(instance); // << ERROR HAPPENS HERE
}
void ScriptingEngine::ShutdownMono() {
}
} // Neutron
my header file, ScriptingEngine.h
//
// Created by aw1lt on 05/12/22.
//
#ifndef NEUTRONENGINE_SCRIPTINGENGINE_H
#define NEUTRONENGINE_SCRIPTINGENGINE_H
#include <string>
namespace Neutron {
class ScriptingEngine {
public:
static void Init();
static void Shutdown();
private:
static void InitMono(std::string path = "../Sandbox/Sandbox/bin/Debug/Sandbox.dll");
static void ShutdownMono();
};
} // Neutron
#endif //NEUTRONENGINE_SCRIPTINGENGINE_H
I also tried the answers on this page.
Thank you, and sorry if this answer is a mess.
A software I am writing is about to take an action that requires the current logged in user is actually the one taking the action. So I want to have windows just ask for the current user's password or biometrics or whatever before the action is allowed to continue.
I used an interop for UserConsentVerifier from another post (Code Below).
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Security.Credentials.UI;
namespace UWPInterop
{
//MIDL_INTERFACE("39E050C3-4E74-441A-8DC0-B81104DF949C")
//IUserConsentVerifierInterop : public IInspectable
//{
//public:
// virtual HRESULT STDMETHODCALLTYPE RequestVerificationForWindowAsync(
// /* [in] */ HWND appWindow,
// /* [in] */ HSTRING message,
// /* [in] */ REFIID riid,
// /* [iid_is][retval][out] */ void** asyncOperation) = 0;
//};
[System.Runtime.InteropServices.Guid("39E050C3-4E74-441A-8DC0-B81104DF949C")]
[System.Runtime.InteropServices.InterfaceType(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIInspectable)]
public interface IUserConsentVerifierInterop
{
IAsyncOperation<UserConsentVerificationResult> RequestVerificationForWindowAsync(IntPtr appWindow, [MarshalAs(UnmanagedType.HString)] string Message, [In] ref Guid riid);
}
//Helper to initialize UserConsentVerifier
public static class UserConsentVerifierInterop
{
public static IAsyncOperation<UserConsentVerificationResult> RequestVerificationForWindowAsync(IntPtr hWnd, string Message)
{
IUserConsentVerifierInterop userConsentVerifierInterop = (IUserConsentVerifierInterop)WindowsRuntimeMarshal.GetActivationFactory(typeof(UserConsentVerifier));
Guid guid = typeof(IAsyncOperation<UserConsentVerificationResult>).GUID;
return userConsentVerifierInterop.RequestVerificationForWindowAsync(hWnd, Message, ref guid);
}
}
}
This works fine if a Windows Hello is setup. It returns DeviceNotPresent when its not or similar errors. Trying to find an alternative where the windows password is provided instead. This code works but I'm not entirely happy with the fact I am using a password in the app's memory. (C++/CLR)
bool ValidateUser(String ^caption, String ^message)
{
bool result = false;
String^ userName = WindowsIdentity::GetCurrent()->Name;
std::wstring strUsername = marshal_as<std::wstring>(userName);
std::wstring strCaption = marshal_as<std::wstring>(caption);
std::wstring strMessage = marshal_as<std::wstring>(message);
CREDUI_INFOW info;
ZeroMemory(&info, sizeof(info));
info.cbSize = sizeof(info);
info.pszMessageText = strMessage.c_str();
info.pszCaptionText = strCaption.c_str();
ULONG authPackage = 0;
LPVOID pOut;
ULONG bufSize;
DWORD inBuffer = 0;
std::vector<uint8_t> credBuffer;
if (!CredPackAuthenticationBufferW(CRED_PACK_PROTECTED_CREDENTIALS, (LPWSTR)strUsername.c_str(), L"", NULL, &inBuffer)
&& ERROR_INSUFFICIENT_BUFFER == ::GetLastError())
{
credBuffer.resize(inBuffer);
if (!CredPackAuthenticationBufferW(CRED_PACK_PROTECTED_CREDENTIALS, (LPWSTR)strUsername.c_str(), L"", credBuffer.data(), &inBuffer))
{
return false;
}
}
DWORD dwResult = CredUIPromptForWindowsCredentialsW(&info, 0, &authPackage, credBuffer.data(), inBuffer, &pOut, &bufSize, NULL, CREDUIWIN_GENERIC | CREDUIWIN_IN_CRED_ONLY);
if (dwResult == ERROR_SUCCESS)
{
DWORD dwUserLength = 0;
DWORD dwDomainLength = 0;
DWORD dwPasswordLength = 0;
try
{
if (!::CredUnPackAuthenticationBufferW(CRED_PACK_PROTECTED_CREDENTIALS, pOut, bufSize, nullptr, &dwUserLength, nullptr, &dwDomainLength, nullptr, &dwPasswordLength)
&& ERROR_INSUFFICIENT_BUFFER == ::GetLastError())
{
std::vector<wchar_t> bufferUser(dwUserLength);
std::vector<wchar_t> bufferDomain(dwDomainLength);
std::vector<wchar_t> bufferPassword(dwPasswordLength);
if (::CredUnPackAuthenticationBufferW(CRED_PACK_PROTECTED_CREDENTIALS, pOut, bufSize, bufferUser.data(), &dwUserLength, bufferDomain.data(), &dwDomainLength, bufferPassword.data(), &dwPasswordLength))
{
HANDLE hToken;
std::wstring strUsername = bufferUser.data();
std::wstring strDomain;
if (bufferDomain.size() == 0)
{
std::wstring::size_type pos = strUsername.find(L'\\');
if (pos != std::wstring::npos)
{
strDomain = strUsername.substr(0, pos);
strUsername = strUsername.substr(pos + 1, strUsername.size() - pos - 1);
}
}
else
{
strDomain = bufferDomain.data();
}
try
{
if (::LogonUserW(strUsername.c_str(), strDomain.c_str(), bufferPassword.data(), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hToken))
result = true;
}
catch (...) // Catch so memory can be cleared
{
}
ClearBuffer(bufferUser.data(), dwUserLength);
ClearBuffer(bufferDomain.data(), dwDomainLength);
ClearBuffer(bufferPassword.data(), dwPasswordLength);
}
}
}
catch(...) // Catch so memory can be cleared
{
}
ClearBuffer(pOut, bufSize);
CoTaskMemFree(pOut);
}
return result;
}
Is there a way to use CredUIPromptForWindowsCredentialsW without unpacking the buffer it returns to verify the login?
How can one get localized name of virtual Known Folder (such as This Computer, Control Panel etc.)?
Eg. for PL-pl they would be, respectively "Ten komputer", "Panel sterowania".
As suggested, I tried to use IKnownFolder from Shell32. There's a 3rd party ready-to-use implementation of these APIs, WinAPICodePack. Sample code:
class Program
{
static void Main(string[] args)
{
// Add from nuget: WindowsAPICodePack-Shell
foreach (var folder in KnownFolders.All)
{
Console.WriteLine($"Canonical name: {folder.CanonicalName}");
Console.WriteLine($"\tPath exists: {folder.PathExists}");
Console.WriteLine($"\tLocalized name: {folder.LocalizedName}");
}
Console.ReadLine();
}
}
Unfortunately, mentioned "This Computer" and "Control Panel" entries does not have localized name.
Note: .NET solution on the bottom.
you need got IShellItem interface for your folder and call IShellItem::GetDisplayName with SIGDN_NORMALDISPLAY
In UI this name is generally ideal for display to the user.
this return localized names
code in c++ can be like this
HRESULT GetKnownFolderName(int csidl, PWSTR* ppszName)
{
PIDLIST_ABSOLUTE pidl;
HRESULT hr = SHGetFolderLocation(0, csidl, 0, 0, &pidl);
if (S_OK == hr)
{
IShellItem* pItem;
hr = SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&pItem));
ILFree(pidl);
if (S_OK == hr)
{
hr = pItem->GetDisplayName(SIGDN_NORMALDISPLAY, ppszName);
pItem->Release();
}
}
return hr;
}
void testDN()
{
if (0 <= CoInitialize(0))
{
PWSTR szName;
// CSIDL_CONTROLS - for "Control Panel"
// CSIDL_DRIVES - for "My Computer"
if (S_OK == GetKnownFolderName(CSIDL_DRIVES, &szName))
{
DbgPrint("%S\n", szName);
CoTaskMemFree(szName);
}
CoUninitialize();
}
}
also if we running only on Vista+ we can use SHGetKnownFolderIDList instead SHGetFolderLocation with FOLDERID_ComputerFolder in place CSIDL_DRIVES or we can get (or already have) IKnownFolder interface first and then got IShellItem from it by IKnownFolder::GetShellItem - so yet two alternative variants begin from vista:
HRESULT GetKnownFolderName(IKnownFolder* kf, PWSTR* ppszName)
{
IShellItem* psi;
HRESULT hr = kf->GetShellItem(KF_FLAG_DEFAULT_PATH, IID_PPV_ARGS(&psi));
if (S_OK == hr)
{
hr = psi->GetDisplayName(SIGDN_NORMALDISPLAY, ppszName);
psi->Release();
}
return hr;
}
HRESULT GetKnownFolderNameVista2(REFKNOWNFOLDERID rfid, PWSTR* ppszName)
{
IKnownFolderManager* mgr;
HRESULT hr = CoCreateInstance(__uuidof(KnownFolderManager), 0, CLSCTX_ALL, IID_PPV_ARGS(&mgr));
if (0 <= hr)
{
IKnownFolder* kf;
hr = mgr->GetFolder(rfid, &kf);
mgr->Release();
if (S_OK == hr)
{
hr = GetKnownFolderName(kf, ppszName);
kf->Release();
}
}
return hr;
}
HRESULT GetKnownFolderNameVista(REFKNOWNFOLDERID rfid, PWSTR* ppszName)
{
PIDLIST_ABSOLUTE pidl;
HRESULT hr = SHGetKnownFolderIDList(rfid, KF_FLAG_NO_ALIAS, 0, &pidl);
if (S_OK == hr)
{
IShellItem* pItem;
hr = SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&pItem));
ILFree(pidl);
if (S_OK == hr)
{
hr = pItem->GetDisplayName(SIGDN_NORMALDISPLAY, ppszName);
pItem->Release();
}
}
return hr;
}
void testDN()
{
if (0 <= CoInitialize(0))
{
PWSTR szName;
if (S_OK == GetKnownFolderNameVista(FOLDERID_ControlPanelFolder, &szName))
{
DbgPrint("%S\n", szName);
CoTaskMemFree(szName);
}
if (S_OK == GetKnownFolderNameVista2(FOLDERID_ComputerFolder, &szName))
{
DbgPrint("%S\n", szName);
CoTaskMemFree(szName);
}
CoUninitialize();
}
}
else one way - use IShellFolder::GetDisplayNameOf with this code will be look like
HRESULT GetKnownFolderName(int csidl, PWSTR* ppszName)
{
PIDLIST_ABSOLUTE pidl;
HRESULT hr = SHGetFolderLocation(0, csidl, 0, 0, &pidl);
if (S_OK == hr)
{
IShellFolder* psf;
PCUITEMID_CHILD pidlLast;
hr = SHBindToParent(pidl, IID_PPV_ARGS(&psf), &pidlLast);
if (S_OK == hr)
{
STRRET str;
hr = psf->GetDisplayNameOf(pidlLast, SHGDN_NORMAL, &str);
psf->Release();
if (hr == S_OK)
{
str.uType == STRRET_WSTR ? *ppszName = str.pOleStr, S_OK : hr = E_FAIL;
}
}
}
return hr;
}
void testDN()
{
if (0 <= CoInitialize(0))
{
PWSTR szName;
if (S_OK == GetKnownFolderName(CSIDL_DRIVES, &szName))
{
DbgPrint("%S\n", szName);
CoTaskMemFree(szName);
}
if (S_OK == GetKnownFolderName(CSIDL_CONTROLS, &szName))
{
DbgPrint("%S\n", szName);
CoTaskMemFree(szName);
}
CoUninitialize();
}
}
You can use WinApiCodePack library (download from Nuget), which provides .NET implementation of several of mentioned before APIs. Sample code would look like following:
private static string GenerateLocalizedName(IKnownFolder shellFolder)
{
// Attempt to obtain localized name of folder
// 1. Directly from KnownFolder
string localizedName = shellFolder.LocalizedName;
// 2. From ShellObject (this solves This Computer and Control Panel issue)
if (String.IsNullOrEmpty(localizedName))
localizedName = (shellFolder as ShellObject)?.Name;
// 3. If folder is not virtual, use its localized name from desktop.ini
if (String.IsNullOrEmpty(localizedName) && Directory.Exists(shellFolder.Path))
{
try
{
localizedName = WinApiInterop.GetLocalizedName(shellFolder.Path);
}
catch
{
// Intentionally left empty
}
}
// 4. If folder is not virtual, use its filename
if (String.IsNullOrEmpty(localizedName) && Directory.Exists(shellFolder.Path))
localizedName = Path.GetFileName(shellFolder.Path);
// 5. If everything else fails, use its canonicalName (eg. MyComputerFolder)
if (String.IsNullOrEmpty(localizedName))
localizedName = shellFolder.CanonicalName;
return localizedName;
}
private void LoadShellFolders()
{
foreach (var shellFolder in KnownFolders.All)
{
string localizedName = GenerateLocalizedName(shellFolder);
string comment = shellFolder.PathExists ? shellFolder.Path : $"shell:{shellFolder.CanonicalName}";
infos.Add(new ShellFolderInfo(shellFolder.CanonicalName,
localizedName,
comment,
shellFolder.CanonicalName,
shellFolder.PathExists ? shellFolder.Path : null));
}
}
Also, the WinApiInterop class, which resolves localized strings from desktop.ini:
static class WinApiInterop
{
[DllImport("shell32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
internal static extern int SHGetLocalizedName(string pszPath, StringBuilder pszResModule, ref int cch, out int pidsRes);
[DllImport("user32.dll", EntryPoint = "LoadStringW", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
internal static extern int LoadString(IntPtr hModule, int resourceID, StringBuilder resourceValue, int len);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryExW")]
internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
internal const uint DONT_RESOLVE_DLL_REFERENCES = 0x00000001;
internal const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
[DllImport("kernel32.dll", ExactSpelling = true)]
internal static extern int FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, ExactSpelling = true)]
internal static extern uint ExpandEnvironmentStrings(string lpSrc, StringBuilder lpDst, int nSize);
public static string GetFullPath(string path)
{
StringBuilder sb = new StringBuilder(1024);
ExpandEnvironmentStrings(path, sb, sb.Capacity);
return sb.ToString();
}
public static string GetLocalizedName(string path)
{
StringBuilder resourcePath = new StringBuilder(1024);
StringBuilder localizedName = new StringBuilder(1024);
int len, id;
len = resourcePath.Capacity;
if (SHGetLocalizedName(path, resourcePath, ref len, out id) == 0)
{
ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity);
IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
if (hMod != IntPtr.Zero)
{
if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0)
{
return localizedName.ToString();
}
FreeLibrary(hMod);
}
}
return null;
}
}
Hi I am trying to access a c++ array of struct from c#. The struct itself also contains an array of strings and a String. The details are below. It's not working.. does not crash but does not transfer the data across (e.g. get nulls in the array, and random number in numberOfRows integer of struct/class). See my comments at the end of the code listing. Any suggestions?
c++ cppClassLib.cpp
// This is the main DLL file.
#include "stdafx.h"
#include <Objbase.h>
#include "cppClassLib.h"
#include <string.h>
//#include <malloc.h>
namespace cppClassLib {
/*
http://msdn.microsoft.com/en-us/magazine/cc164123.aspx
http://stackoverflow.com/questions/9093292/use-a-c-library-from-c-sharp-code
http://stackoverflow.com/questions/5671658/what-does-invalid-managed-unmanaged-type-combination-mean
http://stackoverflow.com/questions/2338146/returning-pointers-from-unmanaged-to-managed-code
CoTaskMemAlloc http://msdn.microsoft.com/en-us/library/windows/desktop/ms692727%28v=vs.85%29.aspx
*/
char *createStr(char *input)
{
int len = strlen(input)+1;
// can't use malloc because it needs to
// be accessible from another process.
// can't use CoTaskMemAlloc because get an
// error when trying to link, not found.
//char *newStr = (char *)CoTaskMemAlloc(len);
//char *newStr = (char *)malloc(len);
char *newStr = (char *)GlobalAlloc(GPTR,len);
//char* newStr = new char[len];
strcpy_s(newStr, len, input);
return newStr;
}
int Class1::getMatrixNumberOfRowsInColumnZero(int maxColumns, Class1::columnT *matrix)
{
if (maxColumns < 1) {
return 0;
}
return matrix[0].numberOfRows;
}
int Class1::getMatrix(int maxColumns, Class1::columnT *matrix)
{
if (maxColumns < 2) {
return 0;
}
int numberOfColumns = 2;
//Class1::columnT *column0 = (Class1::columnT *)GlobalAlloc(GPTR,sizeof(Class1::columnT));
Class1::columnT *column0 = &(matrix[0]);
column0->columnName = createStr("Col0");
int numRows = 2;
column0->numberOfRows = numRows;
char **rows = (char **)GlobalAlloc(GPTR,sizeof(char *)*numRows);
rows[0] = createStr("C0R0");
rows[1] = createStr("C0R1");
column0->rows = rows;
Class1::columnT *column1 = &(matrix[1]);
//Class1::columnT *column1 = (Class1::columnT *)GlobalAlloc(GPTR,sizeof(Class1::columnT));
column1->columnName = createStr("Col1");
numRows = 2;
column1->numberOfRows = numRows;
rows = (char **)GlobalAlloc(GPTR,sizeof(char *)*numRows);
rows[0] = createStr("C1R0");
rows[1] = createStr("C1R1");
column1->rows = rows;
//matrix[0]=column0;
//matrix[1]=column1;
//(matrix[0])->columnName = createStr("Test0");
return numberOfColumns; // 2
}
int Class1::getInt(void)
{
return 1234;
}
char* Class1::getHi(void)
{
//char *result = createStr("Hello");
//return result;
//return createStr("hello");
return createStr("hello");
}
char** Class1::getHeaderList(void)
{
char** list;
list = (char **)GlobalAlloc(GPTR,sizeof(char *)*2);
list[0]=createStr("test1");
list[1]="test2";
return list;
}
int Class1::getHeaderListTwo(int maxsize, char ***result)
{
char** list;
int len = 2;
if (maxsize < len) {
return NULL;
}
list = (char **)GlobalAlloc(GPTR,sizeof(char *)*maxsize);
list[0]=createStr("test01");
list[1]="test02";
for (int i=2; i<maxsize; ++i) {
list[i]="";
}
*result = list;
return len;
}
char* Class1::getHi2(void)
{
return "Hi";
}
char* Class1::getHi3(void)
{
return "Hi!";
}
void Class1::getData(int *totalColumns,
char** headers[2],
char** items[2][3])
{
*totalColumns = 2;
*headers[0]=createStr("Testing");
*headers[1]=createStr("Pets");
*items[0][0]=createStr("test1");
*items[0][1]=createStr("test2");
*items[0][2]=createStr("test3");
*items[1][0]=createStr("Cats");
*items[1][1]=createStr("Dogs");
*items[1][2]=createStr("Fish");
}
}
c++ cppClassLib.h
// cppClassLib.h
#pragma once
using namespace System;
#define DllExport __declspec( dllexport )
// http://msdn.microsoft.com/en-us/library/3y1sfaz2.aspx
namespace cppClassLib {
public class Class1 {
public:
struct columnT {
int numberOfRows;
char **rows;
char *columnName;
};
static DllExport int getMatrix(int maxColumns, columnT *matrix);
static DllExport int getMatrixNumberOfRowsInColumnZero(int maxColumns, columnT *matrix);
static DllExport void getData(int *totalColumns,
char** headers[2],
char** items[2][3]);
static DllExport char *getHi(void);
static DllExport char *getHi2(void);
static DllExport char *getHi3(void);
static DllExport int getInt(void);
static DllExport char** getHeaderList(void);
static DllExport int getHeaderListTwo(int maxsize, char ***result);
};
}
c# Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
/*
http://msdn.microsoft.com/en-us/library/aa288468%28v=vs.71%29.aspx
http://ondotnet.com/pub/a/dotnet/2002/03/18/customcontrols.html?page=2
http://www.codeproject.com/Articles/2995/The-Complete-Guide-to-C-Strings-Part-I-Win32-Chara
http://msdn.microsoft.com/en-us/library/z6cfh6e6.aspx
*/
namespace listViewFromC
{
public partial class Form1 : Form
{
/*
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getInt#Class1#cppClassLib##QAEHXZ")]
public static extern int getInt();
*/
// get EntryPoint using
// "DLL Export Viewer" software
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getHi2#Class1#cppClassLib##SAPADXZ")]
public static extern String getHi2();
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getHi#Class1#cppClassLib##SAPADXZ")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string getHi();
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getHeaderList#Class1#cppClassLib##SAPAPADXZ")]
[return: MarshalAs(UnmanagedType.LPArray,
ArraySubType=UnmanagedType.LPStr, SizeConst=2)]
public static extern String[] getHeaderList();
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getHeaderListTwo#Class1#cppClassLib##SAHHPAPAPAD#Z")]
public static extern int getHeaderListTwo(int maxsize,
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 0)]
ref String[] result
);
[StructLayout(LayoutKind.Sequential)]
public class columnType
{
public int numberOfRows;
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.LPStr)]
public String[] rows;
[MarshalAs(UnmanagedType.LPStr)]
public String columnName;
}
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getMatrix#Class1#cppClassLib##SAHHPAUcolumnT#12##Z")]
public static extern int getMatrix(int maxColumns,
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)]
columnType[] matrix);
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getMatrixNumberOfRowsInColumnZero#Class1#cppClassLib##SAHHPAUcolumnT#12##Z")]
public static extern int getMatrixNumberOfRowsInColumnZero(int maxColumns,
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)]
columnType[] matrix);
/*
[DllImport("kernel32.dll")]
static extern IntPtr GlobalAlloc(uint uFlags, uint dwBytes);
const uint GMEM_FIXED = 0x0000;
const uint GMEM_ZEROINIT = 0x0040;
const uint GPTR = GMEM_FIXED | GMEM_ZEROINIT;
*/
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//label1.Text = getInt().ToString();
label1.Text = getHi2();
listView1.Items.Clear();
listView1.Items.Add(label1.Text);
//listView1.
}
private void button2_Click(object sender, EventArgs e)
{
label1.Text = getHi();
}
private void button3_Click(object sender, EventArgs e)
{
const int maxsize = 2;
String[] headerList = new String[maxsize];
int len = getHeaderListTwo(maxsize, ref headerList);
MessageBox.Show("Got length " + headerList.Length+" ("+len+")");
label1.Text="";
for (int i = 0; i < headerList.Length; ++i)
{
if (headerList[i].Length>0)
{
label1.Text += headerList[i].ToString() + " // ";
}
else
{
label1.Text += " // ";
}
}
}
private void button4_Click(object sender, EventArgs e)
{
int maxColumns=5;
int numberOfColumns = 0;
columnType[] matrix = new columnType[maxColumns];
for (int i = 0; i < maxColumns; ++i)
{
matrix[i] = new columnType();
}
matrix[0].numberOfRows = 1; // pick something just to see if we can get it back
//uint sz = (uint)maxColumns*4;
//IntPtr matrixIP = GlobalAlloc(GPTR, sz);
//columnType[] matrix = matrixIP;
//IntPtr pointerArr = Marshal.AllocHGlobal(maxColumns*4);
//numberOfColumns = getMatrix(maxColumns, matrix);
label1.Text = getMatrixNumberOfRowsInColumnZero(maxColumns,matrix).ToString();
//label1.Text = matrix[0].columnName;
}
}
}
button1, button2 and button3 click events work fine so it shows that some of the c++ marshalling to c# is working. button4_click doesn't work.
label1.Text should return 1 since it's just returning matrix[0].numberOfRows
but in fact it returns some huge number.
Also the getMatrix call if uncommented, also doesn't work, it does run without crash but then all the elements of the array are empty (not filled with the data that getMatrix is supposed to fill them with).
Here is my solution. The only quirk with this solution is the need for fixed length arrays in the struct, which I would have preferred a variable length array but it wouldn't accept a Marshall of LPArray. Maybe it's not possible.
The main problem I had was that I had declared it as a class instead of a struct. The other issue was the array in the struct was an LPArray unmanaged marshall type, to try and have a variable length array but that didn't work since it needed to be a ByValArray (or SafeArray) for it to work.
cppClassLib.h
// cppClassLib.h
#pragma once
using namespace System;
#define DllExport __declspec( dllexport )
// http://msdn.microsoft.com/en-us/library/3y1sfaz2.aspx
#define maxRowsCpp 100
namespace cppClassLib {
public class Class1 {
public:
struct columnT {
int numberOfRows;
char *rows[maxRowsCpp];
char *columnName;
};
struct columnT2 {
int numberOfRows;
};
static DllExport int __thiscall getMatrix(int maxColumns, int maxRows, columnT *matrix[]);
static DllExport int __thiscall getMatrixNumberOfRowsInColumnZero(int maxColumns, columnT matrix[]);
static DllExport int __thiscall getMatrixNumberOfRowsInColumnZero2(int maxColumns, columnT2 matrix[]);
static DllExport void getData(int *totalColumns,
char** headers[2],
char** items[2][3]);
static DllExport char *getHi(void);
static DllExport char *getHi2(void);
static DllExport char *getHi3(void);
static DllExport int getInt(void);
static DllExport char** getHeaderList(void);
static DllExport int getHeaderListTwo(int maxsize, char ***result);
};
}
cppClassLib.cpp
// This is the main DLL file.
#include "stdafx.h"
#include <Objbase.h>
#include "cppClassLib.h"
#include <string.h>
//#include <malloc.h>
namespace cppClassLib {
/*
http://msdn.microsoft.com/en-us/magazine/cc164123.aspx
http://stackoverflow.com/questions/9093292/use-a-c-library-from-c-sharp-code
http://stackoverflow.com/questions/5671658/what-does-invalid-managed-unmanaged-type-combination-mean
http://stackoverflow.com/questions/2338146/returning-pointers-from-unmanaged-to-managed-code
CoTaskMemAlloc http://msdn.microsoft.com/en-us/library/windows/desktop/ms692727%28v=vs.85%29.aspx
*/
char *createStr(char *input)
{
int len = strlen(input)+1;
// can't use malloc because it needs to
// be accessible from another process.
// can't use CoTaskMemAlloc because get an
// error when trying to link, not found.
//char *newStr = (char *)CoTaskMemAlloc(len);
//char *newStr = (char *)malloc(len);
char *newStr = (char *)GlobalAlloc(GPTR,len);
//char* newStr = new char[len];
strcpy_s(newStr, len, input);
return newStr;
}
int Class1::getMatrixNumberOfRowsInColumnZero(int maxColumns, Class1::columnT matrix[])
{
if (maxColumns < 1) {
return 0;
}
return (matrix[0]).numberOfRows;
}
int Class1::getMatrixNumberOfRowsInColumnZero2(int maxColumns, Class1::columnT2 matrix[])
{
if (maxColumns < 1) {
return 0;
}
return (matrix[0]).numberOfRows;
}
int Class1::getMatrix(int maxColumns, int maxRows, Class1::columnT *matrix[])
{
// require at least able to have 2 rows and 2 columns
if ((maxColumns < 2) || (maxRows < 2)) {
return 0;
}
int numberOfColumns = 2;
int numberOfRows = 2;
//return matrix[0].columnName[0];
//Class1::columnT *column0 = (Class1::columnT *)GlobalAlloc(GPTR,sizeof(Class1::columnT));
Class1::columnT *column0 = &(*matrix[0]);
column0->columnName = createStr("Col0");
column0->numberOfRows = numberOfRows;
//char **rows = (char **)GlobalAlloc(GPTR,sizeof(char *)*numRows);
column0->rows[0] = createStr("C0R0");
column0->rows[1] = createStr("C0R1");
Class1::columnT *column1 = &(*matrix[1]);
//Class1::columnT *column1 = (Class1::columnT *)GlobalAlloc(GPTR,sizeof(Class1::columnT));
column1->columnName = createStr("Col1");
column1->numberOfRows = numberOfRows;
//rows = (char **)GlobalAlloc(GPTR,sizeof(char *)*numRows);
column0->rows[0] = createStr("C1R0");
column0->rows[1] = createStr("C1R1");
return numberOfColumns;
}
int Class1::getInt(void)
{
return 1234;
}
char* Class1::getHi(void)
{
//char *result = createStr("Hello");
//return result;
//return createStr("hello");
return createStr("hello");
}
char** Class1::getHeaderList(void)
{
char** list;
list = (char **)GlobalAlloc(GPTR,sizeof(char *)*2);
list[0]=createStr("test1");
list[1]="test2";
return list;
}
int Class1::getHeaderListTwo(int maxsize, char ***result)
{
char** list;
int len = 2;
if (maxsize < len) {
return NULL;
}
list = (char **)GlobalAlloc(GPTR,sizeof(char *)*maxsize);
list[0]=createStr("test01");
list[1]="test02";
for (int i=2; i<maxsize; ++i) {
list[i]="";
}
*result = list;
return len;
}
char* Class1::getHi2(void)
{
return "Hi";
}
char* Class1::getHi3(void)
{
return "Hi!";
}
void Class1::getData(int *totalColumns,
char** headers[2],
char** items[2][3])
{
*totalColumns = 2;
*headers[0]=createStr("Testing");
*headers[1]=createStr("Pets");
*items[0][0]=createStr("test1");
*items[0][1]=createStr("test2");
*items[0][2]=createStr("test3");
*items[1][0]=createStr("Cats");
*items[1][1]=createStr("Dogs");
*items[1][2]=createStr("Fish");
}
}
c# Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
/*
http://msdn.microsoft.com/en-us/library/aa288468%28v=vs.71%29.aspx
http://ondotnet.com/pub/a/dotnet/2002/03/18/customcontrols.html?page=2
http://www.codeproject.com/Articles/2995/The-Complete-Guide-to-C-Strings-Part-I-Win32-Chara
http://msdn.microsoft.com/en-us/library/z6cfh6e6.aspx
*/
namespace listViewFromC
{
public partial class Form1 : Form
{
const int maxRows = 100;
/*
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getInt#Class1#cppClassLib##QAEHXZ")]
public static extern int getInt();
*/
// get EntryPoint using
// "DLL Export Viewer" software
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getHi2#Class1#cppClassLib##SAPADXZ")]
public static extern String getHi2();
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getHi#Class1#cppClassLib##SAPADXZ")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string getHi();
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getHeaderList#Class1#cppClassLib##SAPAPADXZ")]
[return: MarshalAs(UnmanagedType.LPArray,
ArraySubType=UnmanagedType.LPStr, SizeConst=2)]
public static extern String[] getHeaderList();
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?getHeaderListTwo#Class1#cppClassLib##SAHHPAPAPAD#Z")]
public static extern int getHeaderListTwo(int maxsize,
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 0)]
ref String[] result
);
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct columnType
{
public int numberOfRows;
// note: can't marshal rows field as LPArray must be ByValArray or SafeArray
// for maximum of maxRows rows
[MarshalAs(UnmanagedType.ByValArray,
ArraySubType = UnmanagedType.LPStr,
SizeConst=maxRows)]
public String[] rows;
[MarshalAs(UnmanagedType.LPStr)]
public String columnName;
}
[StructLayout(LayoutKind.Sequential)]
public struct columnType2
{
public int numberOfRows;
}
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.ThisCall,
EntryPoint = "?getMatrix#Class1#cppClassLib##SEHHHQAPAUcolumnT#12##Z")]
public static extern int getMatrix(int maxColumns,
int maxRows,
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)]
ref columnType[] matrix);
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.ThisCall,
EntryPoint = "?getMatrixNumberOfRowsInColumnZero#Class1#cppClassLib##SEHHQAUcolumnT#12##Z")]
public static extern int getMatrixNumberOfRowsInColumnZero(int maxColumns,
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.Struct,
SizeParamIndex = 0)]
columnType[] matrix);
[DllImport("cppClassLib.dll",
CallingConvention = CallingConvention.ThisCall,
EntryPoint = "?getMatrixNumberOfRowsInColumnZero2#Class1#cppClassLib##SEHHQAUcolumnT2#12##Z")]
public static extern int getMatrixNumberOfRowsInColumnZero2(int maxColumns,
[MarshalAs(UnmanagedType.LPArray,
SizeParamIndex = 0,
ArraySubType = UnmanagedType.Struct)]
columnType2[] matrix);
/*
[DllImport("kernel32.dll")]
static extern IntPtr GlobalAlloc(uint uFlags, uint dwBytes);
const uint GMEM_FIXED = 0x0000;
const uint GMEM_ZEROINIT = 0x0040;
const uint GPTR = GMEM_FIXED | GMEM_ZEROINIT;
*/
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//label1.Text = getInt().ToString();
label1.Text = getHi2();
listView1.Items.Clear();
listView1.Items.Add(label1.Text);
//listView1.
}
private void button2_Click(object sender, EventArgs e)
{
label1.Text = getHi();
}
private void button3_Click(object sender, EventArgs e)
{
const int maxsize = 2;
String[] headerList = new String[maxsize];
int len = getHeaderListTwo(maxsize, ref headerList);
MessageBox.Show("Got length " + headerList.Length+" ("+len+")");
label1.Text="";
for (int i = 0; i < headerList.Length; ++i)
{
if (headerList[i].Length>0)
{
label1.Text += headerList[i].ToString() + " // ";
}
else
{
label1.Text += " // ";
}
}
}
private void button4_Click(object sender, EventArgs e)
{
int maxColumns=5;
int numberOfColumns = 0;
columnType[] matrix = new columnType[maxColumns];
for (int i = 0; i < maxColumns; ++i)
{
matrix[i] = new columnType();
}
matrix[0].numberOfRows = 1; // pick something just to see if we can get it back
matrix[0].columnName = "ABC";
// numberOfRows must be less than or equal to maxRows
columnType2[] matrix2 = new columnType2[maxColumns];
for (int i = 0; i < maxColumns; ++i)
{
matrix2[i] = new columnType2();
}
matrix2[0].numberOfRows = 1; // pick something just to see if we can get it back
//uint sz = (uint)maxColumns*4;
//IntPtr matrixIP = GlobalAlloc(GPTR, sz);
//columnType[] matrix = matrixIP;
//IntPtr pointerArr = Marshal.AllocHGlobal(maxColumns*4);
//int result = getMatrixNumberOfRowsInColumnZero(maxColumns,matrix);
//label1.Text = result.ToString();
numberOfColumns = getMatrix(maxColumns, maxRows, ref matrix);
label1.Text = matrix[0].columnName;
}
}
}
Question:
In C/C++/C#. (I need it for C#, but C and C++ is also fine).
How can I do a mount -a on Linux.
I mean programmatically, without starting a process like
system("mount -a");
Edit:
Note the "-a".
My question is not actually about how to mount A mountpoint.
It's about how to mount ALL mountpoints in /etc/fstab.
That means parsing the file, extracting the mountpoints, check if already mounted, and only if not already mounted, mount...
Check out the man page by typing man 2 mount. It talks about a system call that can avoid the actual use of system():
#include <sys/mount.h>
int mount(const char *source, const char *target, const char *filesystemtype,
unsigned long mountflags, const void *data);
#Ignacio Vazquez-Abrams:
About your "no way to perform this in C#" ...
Proof that you're wrong by contradiction:
The bellow code is capable of doing the same as
(apt-get install jfsutils)
dd if=/dev/zero of=jfs.dsk bs=1048576 count=150
mkfs.jfs -O jfs.dsk
mkdir -p /mnt/jfs
mount /volumes/jfs.dsk /mnt/jfs -t jfs -o loop
umount /mnt/jfs/
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Syscalls
{
public class Linux
{
// apt-get source util-linux
// ./mount/loop.h
// ./mount/mount.c
// ./mount/lomount.c
// ./include/linux_version.h
// ./lib/linux_version.c
// ./include/linux_reboot.h
protected const int LOOP_SET_FD = 0x4C00;
protected const int LOOP_CLR_FD = 0x4C01;
protected const int LOOP_GET_STATUS = 0x4C03;
protected const int LOOP_SET_STATUS = 0x4C02;
protected const int LOOP_GET_STATUS64 = 0x4C05;
protected const int LOOP_SET_STATUS64 = 0x4C04;
protected const int LO_NAME_SIZE = 64;
protected const int LO_KEY_SIZE = 32;
protected const int PATH_MAX = 4096;
// MS_RELATIME //(default for Linux >= 2.6.30)
// MS_STRICTATIME //(default for Linux < 2.6.30)
// http://harmattan-dev.nokia.com/docs/library/html/manpages/headers/sys/mount.html
public enum MountFlags : ulong
{
MS_RDONLY = 1, // Mount read-only.
MS_NOSUID = 2, // Ignore suid and sgid bits.
MS_NODEV = 4, // Disallow access to device special files.
MS_NOEXEC = 8, // Disallow program execution.
MS_SYNCHRONOUS = 16, // Writes are synced at once.
MS_REMOUNT = 32, // Alter flags of a mounted FS.
MS_MANDLOCK = 64, // Allow mandatory locks on an FS.
S_WRITE = 128, // Write on file/directory/symlink.
S_APPEND = 256, // Append-only file.
S_IMMUTABLE = 512, // Immutable file.
MS_NOATIME = 1024, // Do not update access times.
MS_NODIRATIME = 2048, // Do not update directory access times.
MS_BIND = 4096, // Bind directory at different place.
}; // End Enum MountFlags : ulong
/*
// http://unix.superglobalmegacorp.com/Net2/newsrc/sys/fcntl.h.html
[Flags]
protected enum OpenFlags : int
{
// open-only flags
O_RDONLY = 0x0000, // open for reading only
O_WRONLY = 0x0001, // open for writing only
O_RDWR = 0x0002, // open for reading and writing
O_ACCMODE = 0x0003, // mask for above modes
//#ifdef KERNEL
FREAD = 0x0001,
FWRITE = 0x0002,
//#endif
O_NONBLOCK = 0x0004, // no delay
O_APPEND = 0x0008, // set append mode
//#ifndef _POSIX_SOURCE
O_SHLOCK = 0x0010, // open with shared file lock
O_EXLOCK = 0x0020, // open with exclusive file lock
O_ASYNC = 0x0040, // signal pgrp when data ready
O_FSYNC = 0x0080, // synchronous writes
//#endif
O_CREAT = 0x0200, // create if nonexistant
O_TRUNC = 0x0400, // truncate to zero length
O_EXCL = 0x0800, // error if already exists
//#ifdef KERNEL
FMARK = 0x1000, // mark during gc()
FDEFER = 0x2000, // defer for next gc pass
FHASLOCK = 0x4000 // descriptor holds advisory lock
} // End Enum OpenFlags : int
*/
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
protected struct loop_info
{
public int lo_number;
public System.UIntPtr lo_device; //my_dev_t lo_device; // my_dev_t: long unsigned int
public System.UIntPtr lo_inode; //unsigned long lo_inode;
public System.UIntPtr lo_rdevice; //my_dev_t lo_rdevice;// my_dev_t: long unsigned int
public int lo_offset;
public int lo_encrypt_type;
public int lo_encrypt_key_size;
public int lo_flags;
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_NAME_SIZE)]
public string lo_name; //char lo_name[LO_NAME_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_KEY_SIZE)]
public string lo_encrypt_key; //unsigned char lo_encrypt_key[LO_KEY_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 2)]
public System.UIntPtr[] lo_init; //unsigned long lo_init[2];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 4)]
public string reserved; //char reserved[4];
}; // End Struct loop_info
protected struct loop_info64
{
public System.UInt64 lo_device;
public System.UInt64 lo_inode;
public System.UInt64 lo_rdevice;
public System.UInt64 lo_offset;
public System.UInt64 lo_sizelimit; /* bytes, 0 == max available */
public System.UInt32 lo_number;
public System.UInt32 lo_encrypt_type;
public System.UInt32 lo_encrypt_key_size;
public System.UInt32 lo_flags;
// http://stackoverflow.com/questions/1725855/uint8-t-vs-unsigned-char
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_NAME_SIZE)]
public string lo_file_name; // uint8_t lo_file_name[LO_NAME_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_NAME_SIZE)]
public string lo_crypt_name; // uint8_t lo_crypt_name[LO_NAME_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_KEY_SIZE)]
public string lo_encrypt_key; // uint8_t lo_encrypt_key[LO_KEY_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 2)]
public System.UInt64[] lo_init;
}; // End Struct loop_info64
// http://www.student.cs.uwaterloo.ca/~cs350/common/os161-src-html/kern_2include_2kern_2stat_8h.html
protected static bool S_ISBLK(int mode)
{
const uint S_IFMT = 070000;
const uint S_IFBLK = 050000;
return (((mode) & S_IFMT) == S_IFBLK);
} // End Function S_ISBLK
public static int KERNEL_VERSION()
{
Mono.Unix.Native.Utsname unameres = new Mono.Unix.Native.Utsname();
Mono.Unix.Native.Syscall.uname(out unameres);
System.Text.RegularExpressions.Match ma = System.Text.RegularExpressions.Regex.Match(unameres.release, #"(\d+).(\d+).(\d+)(-)?(\d+)?");
string strMajor = ma.Groups[1].Value;
string strMinor = ma.Groups[2].Value;
string strTiny = ma.Groups[3].Value;
string strPatchlevel = ma.Groups[5].Value;
int a = System.Convert.ToInt32(strMajor);
int b = System.Convert.ToInt32(strMinor);
int c = System.Convert.ToInt32(strTiny);
return KERNEL_VERSION(a, b, c);
} // End Function KERNEL_VERSION
//# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
public static int KERNEL_VERSION(int a, int b, int c)
{
return (((a) << 16) + ((b) << 8) + (c));
}
public static string CreateVirtualDisk(int iSize)
{
string strBaseDirectory = #"/volumes/";
string strFileName = System.Guid.NewGuid().ToString().Replace("-", "") + ".dsk";
string strFileNameAndPath = System.IO.Path.Combine(strBaseDirectory, strFileName);
using (var fs = new System.IO.FileStream(strFileNameAndPath, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
{
fs.SetLength(iSize);
} // End Using fs
return strFileNameAndPath;
} // End Function CreateVirtualDisk
// umount("/mnt/testdisk");
public static bool umount(string strMountPoint)
{
int status = UnsafeNativeMethods.umount(strMountPoint);
if (status == 0)
Console.WriteLine("Successfully unmounted device.");
else
Console.WriteLine("Unmount status: " + status.ToString());
if (status == 0)
return true;
return false;
} // End Function Unmount
public static string find_unused_loop_device()
{
string dev;
int fd;
Mono.Unix.Native.Stat statbuf;
loop_info loopinfo = new loop_info();
loop_info64 lo64 = new loop_info64();
for (int i = 0; i <= 7; i++)
{
dev = "/dev/loop" + i.ToString();
if (System.Convert.ToBoolean(Mono.Unix.Native.Syscall.stat(dev, out statbuf)) == (false && S_ISBLK((int)statbuf.st_mode)))
{
if ((fd = Mono.Unix.Native.Syscall.open(dev, Mono.Unix.Native.OpenFlags.O_RDONLY)) >= 0)
{
// This block was commented out initially
if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS64, ref lo64) == 0)
{
if (Mono.Unix.Native.Syscall.GetLastError() == Mono.Unix.Native.Errno.ENXIO)
{ // probably free
Mono.Unix.Native.Syscall.close(fd);
return dev;
}
}
if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS, ref loopinfo) != 0)
{
// http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/include/asm-generic/errno-base.h#L9
// ENXIO - No such device or address
// The device accessed by a command is physically not present,
// or the address of the device is not present
if (Mono.Unix.Native.Syscall.GetLastError() == Mono.Unix.Native.Errno.ENXIO)
{
// that means the device is most-likely free
Mono.Unix.Native.Syscall.close(fd);
return dev;
}
} // End if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS, ref loopinfo) != 0)
Mono.Unix.Native.Syscall.close(fd);
} // End if ((fd = UnsafeNativeMethods.open(dev, OpenFlags.O_RDONLY)) >= 0)
} // End if (System.Convert.ToBoolean(Mono.Unix.Native.Syscall.stat(dev, out statbuf)) == (false && S_ISBLK((int)statbuf.st_mode)))
} // Next i
return null;
} // End Function find_unused_loop_device
public static int set_loop(string device, string file, int offset, ref int loopro)
{
loop_info loopinfo = new loop_info();
int fd = 0, ffd = 0;
Mono.Unix.Native.OpenFlags mode;
mode = loopro != 0 ? Mono.Unix.Native.OpenFlags.O_RDONLY : Mono.Unix.Native.OpenFlags.O_RDWR;
if (
(
ffd = Mono.Unix.Native.Syscall.open(file, mode)
) < 0
&&
(
(!System.Convert.ToBoolean((int)loopro))
&&
(
Mono.Unix.Native.Syscall.GetLastError() != Mono.Unix.Native.Errno.EROFS
||
(ffd = Mono.Unix.Native.Syscall.open(file, mode = Mono.Unix.Native.OpenFlags.O_RDONLY))
< 0
)
)
) // if
{
Console.WriteLine("Error: file: " + file);
//perror_msg("%s", file);
return 1;
} // End if
if ((fd = Mono.Unix.Native.Syscall.open(device, mode)) < 0)
{
Mono.Unix.Native.Syscall.close(ffd);
Console.WriteLine("Error: device: " + device);
//perror_msg("%s", device);
return 1;
}
loopro = System.Convert.ToInt32(mode == Mono.Unix.Native.OpenFlags.O_RDONLY);
//memset(&loopinfo, 0, sizeof(loopinfo));
//safe_strncpy(loopinfo.lo_name, file, LO_NAME_SIZE);
//strncpy(loopinfo.lo_name, file, LO_NAME_SIZE);
loopinfo.lo_name = string.IsNullOrEmpty(file) ? null : file.Substring(0, Math.Min(file.Length, LO_NAME_SIZE));
loopinfo.lo_offset = offset;
loopinfo.lo_encrypt_key_size = 0;
if (UnsafeNativeMethods.ioctl(fd, LOOP_SET_FD, ffd) < 0)
{
Console.WriteLine("ioctl: LOOP_SET_FD");
//perror_msg("ioctl: LOOP_SET_FD");
Mono.Unix.Native.Syscall.close(fd);
Mono.Unix.Native.Syscall.close(ffd);
return 1;
}
if (UnsafeNativeMethods.ioctl(fd, LOOP_SET_STATUS, ref loopinfo) < 0)
{
int ro = 0;
UnsafeNativeMethods.ioctl(fd, LOOP_CLR_FD, ref ro);
//perror_msg("ioctl: LOOP_SET_STATUS");
Console.WriteLine("ioctl: LOOP_SET_STATUS");
Mono.Unix.Native.Syscall.close(fd);
Mono.Unix.Native.Syscall.close(ffd);
return 1;
}
Mono.Unix.Native.Syscall.close(fd);
Mono.Unix.Native.Syscall.close(ffd);
return 0;
} // End Function set_loop
public static int del_loop(string device)
{
int fd;
if ((fd = Mono.Unix.Native.Syscall.open(device, Mono.Unix.Native.OpenFlags.O_RDONLY)) < 0)
{
//perror_msg("%s", device);
Console.WriteLine("Error description: " + Mono.Unix.Native.Syscall.strerror(Mono.Unix.Native.Syscall.GetLastError()));
return 0;
}
int r = 0;
if (UnsafeNativeMethods.ioctl(fd, LOOP_CLR_FD, ref r) < 0)
{
//perror_msg("ioctl: LOOP_CLR_FD");
Console.WriteLine("ioctl: LOOP_CLR_FD\nError description: " + Mono.Unix.Native.Syscall.strerror(Mono.Unix.Native.Syscall.GetLastError()));
return 0;
}
Mono.Unix.Native.Syscall.close(fd);
Console.WriteLine("Successfully closed loop-device\n");
return 1;
} // End Function del_loop
public static bool mount(string strDevice, string strMountPoint, string strFsType)
{
return mount(strDevice, strMountPoint, strFsType, MountFlags.MS_NOATIME);
}
public static bool mount(string strDevice, string strMountPoint, string strFsType, MountFlags mflags)
{
return mount(strDevice, strMountPoint, strFsType, mflags, IntPtr.Zero);
}
// http://cboard.cprogramming.com/c-programming/126630-using-sys-mount-h-mounting-usb-thumb-drive.html
// http://stackoverflow.com/questions/10458549/mount-usb-drive-in-linux-with-c
// mount("/dev/loop1", "/mnt/testdisk", "vfat");
public static bool mount(string strDevice, string strMountPoint, string strFsType, MountFlags mflags, IntPtr options)
{
// http://linux.die.net/man/2/mount
// MS_RDONLY
// MS_RELATIME (default for Linux >= 2.6.30)
// MS_STRICTATIME (default for Linux < 2.6.30)
if (UnsafeNativeMethods.mount(strDevice, strMountPoint, strFsType, mflags, options) != 0)
{
Mono.Unix.Native.Errno errno = Mono.Unix.Native.Syscall.GetLastError();
if (errno == Mono.Unix.Native.Errno.EBUSY)
{
Console.WriteLine("Mountpoint busy");
}
else
{
Console.WriteLine("Mount error: " + Mono.Unix.Native.Syscall.strerror(errno));
}
return false;
}
else
{
Console.WriteLine("Successfully mounted device !");
}
return true; ;
} // End Function mount
static class UnsafeNativeMethods
{
//string name = "Test";
//TypedReference tf = __makeref(name);
//int c = VarSum(2, __arglist(__makeref(name)));
// http://khason.net/blog/how-to-pinvoke-varargs-variable-arguments-in-c-or-hidden-junk-in-clr/
// //int rv = ioctl(2, 3, __arglist(5, 10));
[System.Runtime.InteropServices.DllImportAttribute("libc", EntryPoint = "ioctl",
CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
public static extern int ioctl(int descriptor, int request, __arglist);
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, int request, int data);
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, int request, ref int data);
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, int request, ref loop_info data);
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, int request, ref loop_info64 data);
//[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
//public static extern int open([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]string pathname, OpenFlags flags);
//[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
//public static extern int close(int fd);
//[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
//public static extern IntPtr read(int fd, IntPtr buffer, UIntPtr count);
///////unsafe public static extern IntPtr read(int fd, void* buffer, UIntPtr count);
// http://linux.die.net/man/2/mount
// http://www.kernel.org/doc/man-pages/online/pages/man2/mount.2.html
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
private static extern int mount(string source, string target, string filesystemtype, UIntPtr mountflags, System.IntPtr data);
//int mount(const char *source, const char *target, const char *filesystemtype, ulong mountflags, const void *data);
public static int mount(string source, string target, string filesystemtype, MountFlags mountflags, System.IntPtr data)
{
System.UIntPtr p = new System.UIntPtr((ulong)mountflags);
return mount(source, target, filesystemtype, p, data);
} // End Function mount
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int umount(string strMountPoint);
// extern int umount (__const char *__special_file);
} // End Class UnsafeNativeMethods
public static void TryMount()
{
const bool SUCCESS = true;
// int iReturnCode = Mono.Unix.Native.Syscall.system("mount -a");
// int iReturnCode = Mono.Unix.Native.Syscall.system("mount /volumes/jfs.dsk /mnt/jfs -t jfs -o loop");
// int iReturnCode = Mono.Unix.Native.Syscall.system("mkfs.jfs -O \"jfs.dsk\"");
string strLoopDeviceToUse = find_unused_loop_device();
string strMountPoint = "/mnt/testdisk";
int ro = 0;
set_loop(strLoopDeviceToUse, "/volumes/testdisk.dsk", 0, ref ro);
string strLoopDeviceToUse2 = find_unused_loop_device();
bool status = false;
int mountAttempts = 0;
do
{
//status = mount("/dev/sda1", "/media/usb0", "vfat", MS_MGC_VAL | MS_NOSUID, "");
status = mount(strLoopDeviceToUse, strMountPoint, "vfat", MountFlags.MS_NOATIME);
if (status != SUCCESS)
System.Threading.Thread.Sleep(1000);
mountAttempts++;
} while (status != SUCCESS && mountAttempts < 3);
} // End Sub TryMount
// In gcc or g++, to show all of the macros that are defined for a given platform:
// gcc -dM -E test.c
// or
// g++ -dM -E test.cpp
// http://manual.cream.org/index.cgi/gnu_dev_major.3
// http://www.gnu.org/software/gnulib/coverage/usr/include/sys/sysmacros.h.gcov.frameset.html
// http://en.wikipedia.org/wiki/C_data_types
protected static uint gnu_dev_major(System.UInt64 __dev)
{
return (uint)((uint)(((__dev >> 8) & 0xfff)) | ((uint)(__dev >> 32) & ~0xfff));
}
protected static uint gnu_dev_minor(System.UInt64 __dev)
{
return (uint)((uint)(__dev & 0xff) | ((uint)(__dev >> 12) & ~0xff));
}
public static string loopfile_from_sysfs(string device)
{
string res = null;
Mono.Unix.Native.Stat st;
System.IntPtr f;
//if (stat(device, &st) || !S_ISBLK(st.st_mode))
//if (System.Convert.ToBoolean(Mono.Unix.Native.Syscall.stat(device, out st)) || !S_ISBLK((int) st.st_mode))
// return null;
Mono.Unix.Native.Syscall.stat(device, out st);
const string _PATH_SYS_DEVBLOCK = "/sys/dev/block";
string strPath = string.Format("{0}/{1}:{2}/loop/backing_file", _PATH_SYS_DEVBLOCK, gnu_dev_major(st.st_rdev), gnu_dev_minor(st.st_rdev));
f = Mono.Unix.Native.Syscall.fopen(strPath, "r");
if (f == IntPtr.Zero)
return null;
Mono.Unix.Native.Syscall.fclose(f);
res = System.IO.File.ReadAllText(strPath);
strPath = null;
return res;
} // End Function loopfile_from_sysfs
public static string loopdev_get_loopfile(string device)
{
string res = loopfile_from_sysfs(device);
if (res == null)
{
loop_info lo = new loop_info();
loop_info64 lo64 = new loop_info64();
int fd;
if ((fd = Mono.Unix.Native.Syscall.open(device, Mono.Unix.Native.OpenFlags.O_RDONLY)) < 0)
return null;
if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS64, ref lo64) == 0)
{
//lo64.lo_file_name[LO_NAME_SIZE-2] = '*';
//lo64.lo_file_name[LO_NAME_SIZE-1] = 0;
//res = strdup((char *) lo64.lo_file_name);
res = lo64.lo_file_name;
Console.WriteLine("LOOP_GET_STATUS64");
}
else if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS, ref lo) == 0)
{
//lo.lo_name[LO_NAME_SIZE-2] = '*';
//lo.lo_name[LO_NAME_SIZE-1] = 0;
//res = strdup((char *) lo.lo_name);
res = lo.lo_name;
Console.WriteLine("LOOP_GET_STATUS");
}
Mono.Unix.Native.Syscall.close(fd);
} // End if (res == null)
return res;
} // End Function loopdev_get_loopfile
public static void TryUnmount()
{
/*
string strMountPoint = "/mnt/testdisk";
umount(strMountPoint);
System.Threading.Thread.Sleep(1000);
del_loop("/dev/loop2");
*/
string xxx = loopdev_get_loopfile("/dev/loop0");
Console.WriteLine("xxx: " + xxx);
}
// kernel-support:
// grep hfs /proc/filesystems
// cat /proc/partitions
// apt-get install hfsprogs
// sudo modprobe hfsplus
// dd if=/dev/zero of=hfsplus.dsk bs=1048576 count=150
// mkfs.hfsplus /volumes/hfsplus.dsk
// mkfs.hfsplus hfsplus.dsk
// apt-get install jfsutils
// dd if=/dev/zero of=jfs.dsk bs=1048576 count=150
// mkfs.jfs -O jfs.dsk
// mkdir -p /mnt/jfs
// mount /volumes/jfs.dsk /mnt/jfs -t jfs -o loop
// umount /mnt/jfs/
// mkdir -p /mnt/hfsplus
// mount -t hfsplus /volumes/hfsplus.dsk /mnt/hfsplus/ -o loop
//
} // End Class Linux
} // End Namespace Syscalls
// http://ubuntuforums.org/showthread.php?t=135113
// http://stackoverflow.com/questions/7027151/call-expect-script-in-c-process
// http://linux.die.net/man/1/expect
// http://linux.die.net/man/3/libexpect
// http://linuxcommand.org/man_pages/losetup8.html
// losetup /dev/loop0 /file
// losetup -d /dev/loop0
// http://linux.about.com/library/cmd/blcmdl8_losetup.htm
To perma-mount it in fstab, you need to get the partition uuid (blkid)
getmntent can help you read /etc/fstab (and then use the mount function in the other answers).