Import function from dll in c# with ulong* parameter - c#

I'm developing in windows 7 and c# in visual studio 2015;
i have a problem when i import a function used in a dll in my other c program:
unsigned long ulaVersion[2];
MHM_GetDLLVersion(ulaVersion);
cout << "MHM_1SL_interface.dll Version: " << ulaVersion[1] << "." << ulaVersion[0] << endl << endl;
In the header file :
unsigned long __stdcall MHM_GetDLLVersion(unsigned long *pulDllVersion);
the function works and print 1.8.
Well, now i try to use with c#
[DllImport(#"MHM_1SL_interface.dll", CallingConvention = CallingConvention.StdCall)]
public static extern UInt32 MHM_GetDLLVersion(ref UInt32 ulaVersion);
I try to use this but i have received "8" with result...
Where i'm wronging?
Which is the right method to pass the ulong* parameter to the function in c#?

Related

Mono MSC C# compiler how to add assembly references

So im trying to run a .NET C# script in mono , but i get errors of missing assemblies...
C:\Users\user\Documents\GitHub\callCsharp-fromCPP\CMonoTest\Mp3ToSpeech.cs(1,14): error CS0234: The type or namespace name `Speech' does not exist in the namespace `System'. Are you missing an assembly reference?
C:\Users\user\Documents\GitHub\callCsharp-fromCPP\CMonoTest\Mp3ToSpeech.cs(6,7): error CS0246: The type or namespace name `NAudio' could not be found. Are you missing an assembly reference?
C:\Users\user\Documents\GitHub\callCsharp-fromCPP\CMonoTest\SeleniumWebdriver.cs(1,7): error CS0246: The type or namespace name `OpenQA' could not be found. Are you missing an assembly reference?
...
here is my current C++ code
#include <windows.h>
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#include <cstdlib>
#include <string>
#include <iostream>
#include <sstream>
#include <direct.h>
#include <filesystem>
#pragma comment(lib, "mono-2.0-boehm.lib") // replaced from mono-2.0.lib
#pragma comment(lib, "mono-2.0-sgen.lib") // It is new with GC code library GC from Gnu Compilication
#pragma comment(lib , "MonoPosixHelper.lib")
std::string get_working_path()
{
char temp[260]; // max windows path length
return (_getcwd(temp, sizeof(temp)) ? std::string(temp) : std::string(""));
}
int main(int argc, char* argv[])
{
#pragma region Load and compile the script
std::string Mp3Path = std::filesystem::path(get_working_path()).parent_path().string() + R"(\CMonoTest\ChromeDriverInstaller.cs )";
std::string ProgramPath = std::filesystem::path(get_working_path()).parent_path().string() + R"(\CMonoTest\Program.cs )";
std::string ChromeInstPath = std::filesystem::path(get_working_path()).parent_path().string() + R"(\CMonoTest\Mp3ToSpeech.cs )";
std::string SeleniumWebPath = std::filesystem::path(get_working_path()).parent_path().string() + R"(\CMonoTest\SeleniumWebdriver.cs )";
//C:\Users\user\.nuget\packages\dotnetseleniumextras.waithelpers\3.11.0\lib\net45\SeleniumExtras.WaitHelpers.dll
//C:\Users\user\.nuget\packages\naudio\2.1.0\lib\net6.0\NAudio.dll
//C:\Users\user\.nuget\packages\selenium.support\4.4.0\lib\net5.0\WebDriver.Support.dll
//C:\Users\user\.nuget\packages\selenium.webdriver\4.4.0\lib\net5.0\WebDriver.dll
//C:\Users\user\.nuget\packages\system.speech\6.0.0\lib\net6.0\System.Speech.dll
std::string references = R"(-r:C:\Users\user\.nuget\packages\dotnetseleniumextras.waithelpers\3.11.0\lib\net45\SeleniumExtras.WaitHelpers.dll
-r:C:\Users\user\.nuget\packages\naudio\2.1.0\lib\net6.0\NAudio.dll -r:/C:\Users\user\.nuget\packages\selenium.support\4.4.0\lib\net5.0\WebDriver.Support.dll
-r:C:\Users\user\.nuget\packages\selenium.webdriver\4.4.0\lib\net5.0\WebDriver.dll -r:C:\Users\user\.nuget\packages\system.speech\6.0.0\lib\net6.0\System.Speech.dll)";
std::string command = "mcs " + ProgramPath + ChromeInstPath + SeleniumWebPath + Mp3Path + references + R"( -target:library)";
//Compile the script
std::string Command = std::string(R"(cd C:\Program Files (x86)\Mono\bin && )") + command;
system(Command.c_str());
#pragma endregion
#pragma region Init mono runtime
mono_set_dirs("C:\\Program Files (x86)\\Mono\\lib",
"C:\\Program Files (x86)\\Mono\\etc");
//Init a domain
MonoDomain* domain;
domain = mono_jit_init("MonoScriptTry");
if (!domain)
{
std::cout << "mono_jit_init failed" << std::endl;
system("pause");
return 1;
}
//Open a assembly in the domain
MonoAssembly* assembly;
std::string assemblyPath = std::filesystem::path(get_working_path()).parent_path().string() + R"(\CMonoTest\Program.dll)";
assembly = mono_domain_assembly_open(domain, assemblyPath.c_str());
if (!assembly)
{
std::cout << "mono_domain_assembly_open failed" << std::endl;
system("pause");
return 1;
}
//Get a image from the assembly
MonoImage* image;
image = mono_assembly_get_image(assembly);
if (!image)
{
std::cout << "mono_assembly_get_image failed" << std::endl;
system("pause");
return 1;
}
#pragma endregion
#pragma region Run a static method
{
//Build a method description object
MonoMethodDesc* TypeMethodDesc;
const char* TypeMethodDescStr = "Program:StartWebRegister()";
TypeMethodDesc = mono_method_desc_new(TypeMethodDescStr, NULL);
if (!TypeMethodDesc)
{
std::cout << "mono_method_desc_new failed" << std::endl;
system("pause");
return 1;
}
//Search the method in the image
MonoMethod* method;
method = mono_method_desc_search_in_image(TypeMethodDesc, image);
if (!method)
{
std::cout << "mono_method_desc_search_in_image failed" << std::endl;
system("pause");
return 1;
}
//run the method
std::cout << "Running the static method: " << TypeMethodDescStr << std::endl;
mono_runtime_invoke(method, nullptr, nullptr, nullptr);
}
#pragma endregion
#pragma endregion
system("pause");
return 0;
}
Im trying to reference assemblies using -r: , using full path where they are located , but it doesnt work and i still get errors.
I Tried using msbuild , to build .csproj with instead of msc (see my other question Embedding mono in C++ , Could not load file or assembly 'System.Runtime' or one of its dependencies) but it cant call the static method
So my question is how can i reference the assemblies. Is there a easy way to do that in mono if you are using nuget? I saw on the docs that you can call packages using -pkg: if they have a .pc file (which none of .nuget have)
For those who have also problems with this , my problem was that
std::string references = R"(-r:C:\Users\user\.nuget\packages\dotnetseleniumextras.waithelpers\3.11.0\lib\net45\SeleniumExtras.WaitHelpers.dll
-r:C:\Users\user\.nuget\packages\naudio\2.1.0\lib\net6.0\NAudio.dll -r:C:\Users\user\.nuget\packages\selenium.support\4.4.0\lib\net5.0\WebDriver.Support.dll
-r:C:\Users\user\.nuget\packages\selenium.webdriver\4.4.0\lib\net5.0\WebDriver.dll -r:C:\Users\user\.nuget\packages\system.speech\6.0.0\lib\net6.0\System.Speech.dll)";
wasnt respecting this format
msc -r:"reference\to\assembly.dll"
it should be in quotes even if the path doesnt have any spaces.
This is a problem only when you want to use full path , as
msc -r:System.Assembly.dll
works fine!

RunTime Error in Unity when I'm using a function from a C++ Dll

I'm using a Dll created from a C++ file. When I either put the .dll and .lib files in my Unity-project folder or when I use the function that I need, Unity crashes and I can't open the project untile I remove the .dll or delete the function from the c# script.
This function works well on C++, both in Visual Studio and in Dev-C++
PS: Assets/alzBraccioCorretto.json is the file that I need to read
I've tried the same procedure for more simple dlls and it worked fine, so I don't know what I'm missing with this one.
In the Unity script I wrote
[DllImport("QuartaLibreria.dll", CharSet = CharSet.Unicode)]
static extern int LeggiFile(string nomeFile);
Text testo;
unsafe void Start()
{
testo = GetComponent<Text>();
int temp = 0;
temp = LeggiFile("Assets/alzBraccioCorretto.json");
testo.text = temp.ToString();
}
In the header of the library I have
#define QUARTALIBRERIA_API __declspec(dllexport)
//all the other headers and #include
int converti_int(string frame);//returns reads a line and returns an int
int leggiFile(string nomeFile);
extern "C" {
QUARTALIBRERIA_API int LeggiFile(wstring nomeFile);
}
In the cpp of the library I have
int leggiFile(string nomeFile) {
ifstream _stream(nomeFile.c_str());
int temp;
_stream >> temp >> temp >> temp >> temp >> temp;
return temp;
}
QUARTALIBRERIA_API int LeggiFile(wstring nomeFile){
std::string str = std::string(nomeFile.begin(), nomeFile.end());
int daRit = leggiFile(str);
return daRit;
}

Create DLL in C++ and using in C# with BSTR* return function

I'm meeting the problem when creating DLL in C++ and using in C# with an array of String.
Here is my DLL code in c++, Im creating info.dll
extern "C" { __declspec(dllexport) BSTR* GetInfos()
{
string result1 = "ABC", result2="DEF";`
BSTR* MyBstr = new BSTR[2];
MyBstr[0] = SysAllocString(CA2W(result1.c_str()));
MyBstr[1] = SysAllocString(CA2W(result2.c_str()));
return MyBstr;
}
}
In c# code, Im creating DLL import to get this Function in class getInfoFromDll:
[DllImport("info.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string[] GetInfos();
I can't get any results when call GetInfos() from my DLL.
String[] s = getInfoFromDll.GetInfos();

How to pass a String pointer/reference from C++ to C#?

I'm writing a C# com library. I need a function that takes string from C++ and modifies string, after I have modified this string in my library modified value should be usable by C++ code. Here is my function (it's not working),
C# Interface,
Int32 testString([param: MarshalAs(UnmanagedType.LPStr)] ref string strData);
C# Implementation,
class Test:ITest
{
public int testString(ref string strData)
{
strData = strData + strData;
Console.WriteLine("strData in lib :"+strData);
return 0;
}
}
C++ code (I missed the parts, registering com lib, importing tlb etc.),
ITest * mInterface = NULL;
//...initilizing interface , skipped since this parts has no problem
LPSTR tt = "OLA";
mInterface ->testString(&tt); //It crashes when comes here ..
cout << tt <<endl;
I have found a solution to my problem , I am not sure but maybe we cannot initilized object from c++ to c# using com interop.If a use a null pointer to c# com , and initilize that pointer inside c# library code it worked .You can see the sample below ;
c# library interface
int testString(out string val);
c# library imp
class Test
{
int testString(out string val)
{
val = "Hello world"; //now user of this method can use val
return 0;
}
}
c++ usage
#pragma warning (disable: 4278)
#import "../yourtlb.tlb" no_namespace named_guids
ITestInterface * fpInterface = NULL;
int retval = 1;
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(CLSID_Test ,
NULL, CLSCTX_INPROC_SERVER,
IID_ITestInterface, reinterpret_cast<void**>(&fpInterface));
if(!FAILED(hr))
{
BSTR val = NULL;
int ret = fpInterface->testString(val);
cout <<val<<endl;
}
Of course before using code in c++ , dont forget to generate tlb file from your c# project , and dont forget to edit registry files.Here you can see a good sample

Open a URL in a new browser process

I need to open a URL in a new browser process. I need to be notified when that browser process quits. The code I'm currently using is the following:
Process browser = new Process();
browser.EnableRaisingEvents = true;
browser.StartInfo.Arguments = url;
browser.StartInfo.FileName = "iexplore";
browser.Exited += new EventHandler(browser_Exited);
browser.Start();
Clearly, this won't due because the "FileName" is fixed to iexplore, not the user's default web browser. How do I figure out what the user's default web browser is?
I'm running on Vista->forward. Though XP would be nice to support if possible.
A bit more context: I've created a very small stand-alone web server that serves some files off a local disk. At the end of starting up the server I want to start the browser. Once the user is done and closes the browser I'd like to quit the web server. The above code works perfectly, other than using only IE.
Thanks in advance!
Ok. I now have working C# code to do what I want. This will return the "command line" you should run to load the current default browser:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
namespace testDefaultBrowser
{
public enum ASSOCIATIONLEVEL
{
AL_MACHINE,
AL_EFFECTIVE,
AL_USER,
};
public enum ASSOCIATIONTYPE
{
AT_FILEEXTENSION,
AT_URLPROTOCOL,
AT_STARTMENUCLIENT,
AT_MIMETYPE,
};
[Guid("4e530b0a-e611-4c77-a3ac-9031d022281b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IApplicationAssociationRegistration
{
void QueryCurrentDefault([In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
[In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONTYPE atQueryType,
[In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONLEVEL alQueryLevel,
[Out, MarshalAs(UnmanagedType.LPWStr)] out string ppszAssociation);
void QueryAppIsDefault(
[In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
[In] ASSOCIATIONTYPE atQueryType,
[In] ASSOCIATIONLEVEL alQueryLevel,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
[Out] out bool pfDefault);
void QueryAppIsDefaultAll(
[In] ASSOCIATIONLEVEL alQueryLevel,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
[Out] out bool pfDefault);
void SetAppAsDefault(
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszSet,
[In] ASSOCIATIONTYPE atSetType);
void SetAppAsDefaultAll(
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName);
void ClearUserAssociations();
}
[ComImport, Guid("591209c7-767b-42b2-9fba-44ee4615f2c7")]//
class ApplicationAssociationRegistration
{
}
class Program
{
static void Main(string[] args)
{
IApplicationAssociationRegistration reg =
(IApplicationAssociationRegistration) new ApplicationAssociationRegistration();
string progID;
reg.QueryCurrentDefault(".txt",
ASSOCIATIONTYPE.AT_FILEEXTENSION,
ASSOCIATIONLEVEL.AL_EFFECTIVE,
out progID);
Console.WriteLine(progID);
reg.QueryCurrentDefault("http",
ASSOCIATIONTYPE.AT_URLPROTOCOL,
ASSOCIATIONLEVEL.AL_EFFECTIVE,
out progID);
Console.WriteLine(progID);
}
}
}
Whew! Thanks everyone for help in pushing me towards the right answer!
If you pass a path of the known file type to the (file) explorer application, it will 'do the right thing', e.g.
Process.Start("explorer.exe", #"\\path.to\filename.pdf");
and open the file in the PDF reader.
But if you try the same thing with a URL, e.g.
Process.Start("explorer.exe", #"http://www.stackoverflow.com/");
it fires up IE (which isn't the default browser on my machine).
I know doesn't answer the question, but I thought it was an interesting sidenote.
The way to determine the default browser is explained in this blog post:
http://ryanfarley.com/blog/archive/2004/05/16/649.aspx
From the blog post above:
private string getDefaultBrowser()
{
string browser = string.Empty;
RegistryKey key = null;
try
{
key = Registry.ClassesRoot.OpenSubKey(#"HTTP\shell\open\command", false);
//trim off quotes
browser = key.GetValue(null).ToString().ToLower().Replace("\"", "");
if (!browser.EndsWith("exe"))
{
//get rid of everything after the ".exe"
browser = browser.Substring(0, browser.LastIndexOf(".exe")+4);
}
}
finally
{
if (key != null) key.Close();
}
return browser;
}
Ok, I think I might have found it - IApplicationAssociationRegistration::QueryCurrentDefault [1]. According to the docs this is what is used by ShellExecute. I'll post code when I get it to work, but I'd be interested if others think this is the right thing to use (BTW, I'm Vista or greater for OS level).
[1]: http://msdn.microsoft.com/en-us/library/bb776336(VS.85).aspx QueryCurrentDefault
Ok. Been away on the conference circuit for a week, now getting back to this. I can do this with C++ now - and it even seems to behave properly! My attempts to translate this into C# (or .NET) have all failed however (Post On Question).
Here is the C++ code for others that stumble on this question:
#include "stdafx.h"
#include <iostream>
#include <shobjidl.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <atlbase.h>
#include <atlstr.h>
#include <AtlDef.h>
#include <AtlConv.h>
using namespace std;
using namespace ATL;
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = CoInitialize(NULL);
if (!SUCCEEDED(hr)) {
cout << "Failed to init COM instance" << endl;
cout << hr << endl;
}
IApplicationAssociationRegistration *pAAR;
hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration),
(void**) &pAAR);
if (!SUCCEEDED(hr))
{
cout << "Failed to create COM object" << endl;
cout << hr << endl;
return 0;
}
LPWSTR progID;
//wchar_t *ttype = ".txt";
hr = pAAR->QueryCurrentDefault (L".txt", AT_FILEEXTENSION, AL_EFFECTIVE, &progID);
if (!SUCCEEDED(hr)) {
cout << "Failed to query default for .txt" << endl;
cout << hr << endl;
}
CW2A myprogID (progID);
cout << "Result is: " << static_cast<const char*>(myprogID) << endl;
/// Now for http
hr = pAAR->QueryCurrentDefault (L"http", AT_URLPROTOCOL, AL_EFFECTIVE, &progID);
if (!SUCCEEDED(hr)) {
cout << "Failed to query default for http" << endl;
cout << hr << endl;
}
CW2A myprogID1 (progID);
cout << "Result is: " << static_cast<const char*>(myprogID1) << endl;
return 0;
}
I will post the C# code when I finally get it working!
I've written this code for a project once... it keeps in mind any additional parameters set for the default browser. It was originally created to open HTML documentation in a browser, for the simple reason I always set my default program for HTML to an editor rather than a browser, and it annoys me to no end to see some program open its HTML readme in my text editor. Obviously, it works perfectly for URLs too.
/// <summary>
/// Opens a local file or url in the default web browser.
/// </summary>
/// <param name="path">Path of the local file or url</param>
public static void openInDefaultBrowser(String pathOrUrl)
{
pathOrUrl = "\"" + pathOrUrl.Trim('"') + "\"";
RegistryKey defBrowserKey = Registry.ClassesRoot.OpenSubKey(#"http\shell\open\command");
if (defBrowserKey != null && defBrowserKey.ValueCount > 0 && defBrowserKey.GetValue("") != null)
{
String defBrowser = (String)defBrowserKey.GetValue("");
if (defBrowser.Contains("%1"))
{
defBrowser = defBrowser.Replace("%1", pathOrUrl);
}
else
{
defBrowser += " " + pathOrUrl;
}
String defBrowserProcess;
String defBrowserArgs;
if (defBrowser[0] == '"')
{
defBrowserProcess = defBrowser.Substring(0, defBrowser.Substring(1).IndexOf('"') + 2).Trim();
defBrowserArgs = defBrowser.Substring(defBrowser.Substring(1).IndexOf('"') + 2).TrimStart();
}
else
{
defBrowserProcess = defBrowser.Substring(0, defBrowser.IndexOf(" ")).Trim();
defBrowserArgs = defBrowser.Substring(defBrowser.IndexOf(" ")).Trim();
}
if (new FileInfo(defBrowserProcess.Trim('"')).Exists)
Process.Start(defBrowserProcess, defBrowserArgs);
}
}
Short answer, you can't.
If the default browser is, say, Firefox, and the user already has a Firefox instance running, it will just be opened in another window or tab of the same firefox.exe process, and even after they close your page, the process won't exit until they close every window and tab. In this case, you would receive notification of the process exiting as soon as you started it, due to the temporary firefox.exe proc that would marshal the URL to the current process. (Assuming that's how Firefox's single instance management works).

Categories