Get the name of the exe which invoke my dll - c#

I have a C# Project which Invoke a C++ dll
And before returning the value in the C++ dll, I would like to check the name of the C# exe which invoke my method. Can you advice me please?
I Load the c++ dll like this:
[DllImport("MindSystem.dll",
EntryPoint = "MindSystemPlusPlus",
CharSet = CharSet.Ansi,
CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
public static extern IntPtr MindSystemPlusPlus(int value);
And when I load it, I want that the c++ dll check the name of the exe which invoke it
Edit: I tried this code, but the output in c# is in strange characters :
char fileName[MAX_PATH + 1];
GetModuleFileNameA(NULL, fileName, MAX_PATH + 1);
return fileName;

You should try using GetModuleFileName() function. You can get the full path of the exe. Keep in mind if your DLL is loaded by more than one applications then returned file path will refer to only one of them.

You can call GetModuleFileName function. NULL as first parameter means that path to the executable of the current process is requested.
std::string expectedPath("C:\\expected.exe");
TCHAR fileName[MAX_PATH + 1];
DWORD charsWritten = GetModuleFileName(NULL, fileName, MAX_PATH + 1);
if (charsWritten != 0)
{
if (expectedPath == fileName)
{
// do something
}
}

#include <windows.h>
#include <shellapi.h>
int argc = 0;
auto wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
auto program_path = wargv[0];
...
LocalFree(wargv);
documentation:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156(v=vs.85).aspx

It depends.
If you are using c++ with /clr you can use read the name of the Process returned from Process::GetCurrentProcess().
In native code in Windows you can use GetModuleFileName()
In Linux or MAC there are different options depending on your platform.

Related

C++ Dll in WPF C# application

After looking for solutions already proposed to a similar question to mine,
and since this is the first time I'm using a non-.NET DLL in a .NET application,
I really need your help.
I have an WPF application, using MVVM Pattern, and in my ViewModel class I need to use a DLL done in C++ to recover a token. I have an example in C++ that uses this DLL, so I have the method's names, but I can't do the same in C#. I know that I must use DllImport to use this methods, but how implement it and use the pointer in C#??
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <string>
int main()
{
HINSTANCE hinst = NULL;
typedef bool ( *GetTokenProto )( char ** );
typedef void ( *FreeTokenProto )( char * );
GetTokenProto GetToken;
FreeTokenProto FreeToken;
std::string str = "DllName.dll";
std::string token;
if ( (hinst = LoadLibraryA(str.c_str()) ) )
{
GetToken = (GetTokenProto) GetProcAddress(hinst, "GetToken");
FreeToken = (FreeTokenProto) GetProcAddress(hinst, "FreeToken");
if (GetToken && FreeToken)
{
char *buf;
if (GetToken(&buf))
{
token = buf;
FreeToken(buf);
std::cout << "Token:" << token << std::endl;
}
else
{
std::cerr << "DLL loaded but no token" << std::endl;
exit(1);
}
}
else
{
std::cerr << "DLL loaded but missing proc address(es)" << std::endl;
exit(1);
}
FreeLibrary(hinst);
}
else
{
std::cerr << "Failed to load DLL" << std::endl;
exit(1);
}
return 0;
}
Update
[DllImport("DllName.dll", EntryPoint = "GetToken", CallingConvention = CallingConvention.Cdecl)]
public static extern bool get_token(ref string token);
[DllImport("DllName.dll", EntryPoint = "FreeToken", CallingConvention = CallingConvention.Cdecl)]
public static extern void free_token(ref string token);
public static string a_token;
public string get_token_method()
{
try
{
string buffer = null;
if (get_token(ref buffer))
{
a_token = buffer;
free_token(ref buffer);
Debug.WriteLine("token : " + a_refresh_token);
}
else
{
Debug.WriteLine("DLL Loaded but no token");
}
}
catch (Exception ex)
{
Debug.WriteLine("\n" + ex.Message);
}
return a_refresh_token;
}
The error
I have an Exception "System.DllNotFoundException" : Unable to load DLL
'DllName.dll': The specified module could not be found. (Exception
from HRESULT: 0x8007007E).
Dll file is in the same folder of the .exe (..\bin\Debug)
If your DLL is in the same Directory, there are still a few things that might be the problem.
Firstly
The DLL may have dependencies
The native DLL you use may have other dependencies which have to be
installed (try Dependency Walker). If the native DLL requires for
example registry settings, config files etc. these should also be
present. It should be distributed to user machines the same way you
installed it on the dev machine.
Most likely you are missing the C++ redistributable package (which one I'm not sure) however Dependency Walker should tell you.
Secondly
It could be targeting a different bitness i.e x86 x64, so I'd try changing your project to see if that helps
Right click your project, and select properties.
In properties, select the build tab. Under platform target, select x86.
Hit Ctrl+Shift+S to save all files, right click the solution and select "Clean" to get rid of old binaries. Any builds after that
should be 32 bit
You can use SetDllDirectory method to set path of your dll before invoking your dll
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
You should also use StringBuilder instead of string on c# side if you want to get string from c++.

C# call to a C DLL is only partly functional

I am learning C# from my C++/CLR background by rewriting a sample C++/CLR project in C#.
The project is a simple GUI (using Visual Studio/ Windows Forms) that performs calls to a DLL written in C (in fact, in NI LabWindows/CVI but this is just ANSI C with custom libraries). The DLL is not written by me and I cannot perform any changes to it because it is also used elsewhere.
The DLL contains functions to make an RFID device perform certain functions (like reading/writing RFID tag etc). In each of these functions, there is always a call to another function that performs writing to a log file. If the log file is not present, it is created with a certain header and then data is appended.
The problem is: the C++/CLR project works fine.
But, in the C# one, the functions work (the RFID tag is correctly written/read etc.) but there is no activity regarding the log file!
The declarations for DLL exports look like this (just one example, there are more of them, of course):
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300]);
int save_Logdatei(char path_Logdatei[], char Funktion[], char Mitteilung[]);
The save_Logdatei function is called during execution of Magnetfeld_einschalten like this:
save_Logdatei(path_Logfile_RFID, "Magnetfeld_einschalten", "OK");
In the C++/CLR project, I declared the function like this:
#ifdef __cplusplus
extern "C" {
#endif
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300]);
#ifdef __cplusplus
}
#endif
then a simple call to the function is working.
In the C# project, the declaration goes like:
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "Magnetfeld_einschalten", CharSet = CharSet.Ansi, ExactSpelling = false)]
private static extern int Magnetfeld_einschalten(string path_Logfile_RFID);
and, as I said, although the primary function is working (in this case, turning on the magnetic field of the RFID device), the logging is never done (so, the internal DLL call to save_Logdatei is not executing correctly).
The relevant code in the Form constructor is the following:
pathapp = Application.StartupPath;
pathlog = string.Format("{0}\\{1:yyyyMMdd}_RFID_Logdatei.dat", pathapp, DateTime.Now);
//The naming scheme for the log file.
//Normally, it's autogenerated when a `save_Logdatei' call is made.
Magnetfeld_einschalten(pathlog);
What am I missing? I have already tried using unsafe for the DLL method declaration - since there is a File pointer in save_Logdatei - but it didn't make any difference.
===================================EDIT==================================
Per David Heffernan's suggestion, i have tried to recreate the problem in an easy to test way. For this, i have created a very simple DLL ("test.dll") and I have stripped it completely from the custom CVI libaries, so it should be reproducible even without CVI. I have uploaded it here. In any case, the code of the DLL is:
#include <stdio.h>
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300]);
int save_Logdatei(char path_Logdatei[], char Funktion[], char Mitteilung[]);
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300])
{
save_Logdatei(path_Logfile_RFID, "Opening Magnet Field", "Success");
return 0;
}
int save_Logdatei(char path_Logdatei[], char Funktion[], char Mitteilung[])
{
FILE *fp; /* File-Pointer */
char line[700]; /* Zeilenbuffer */
char path[700];
sprintf(path,"%s\\20160212_RFID_Logdatei.dat",path_Logdatei);
fp = fopen (path, "a");
sprintf(line, "Just testing");
sprintf(line,"%s %s",line, Funktion);
sprintf(line,"%s %s",line, Mitteilung);
fprintf(fp,"%s\n",line);
fclose(fp);
return 0;
}
The C# code is also stripped down and the only thing i have added to the standard Forms project, is Button 1 (and the generated button click as can be seen). The code is this:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestDLLCallCSharp
{
public partial class Form1 : Form
{
public int ret;
public string pathapp;
public string pathlog;
[DllImport("test", CallingConvention = CallingConvention.StdCall, EntryPoint = "Magnetfeld_einschalten", CharSet = CharSet.Ansi, ExactSpelling = false)]
private static extern int Magnetfeld_einschalten(string path_Logfile_RFID);
public Form1()
{
pathapp = #"C:\ProgramData\test";
pathlog = string.Format("{0}\\20160212_RFID_Logdatei.dat", pathapp);
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
ret = Magnetfeld_einschalten(pathlog);
}
}
}
As can be seen, I have avoided using an automatic naming scheme for the log file (normally i use the date) and in both the dll and the C# code, the log file is "20160212_RFID_Logdatei.dat". I have also avoided using the app path as the directory where to put the log file and instead I have opted for a folder named test i created in ProgramData
Again, no file is created at all
This looks like a simple typo in your calling code. Instead of:
ret = Magnetfeld_einschalten(pathlog);
you mean to write:
ret = Magnetfeld_einschalten(pathapp);
In the C# code, these two strings have the following values:
pathapp == "C:\ProgramData\\test"
pathlog == "C:\ProgramData\\test\\20160212_RFID_Logdatei.dat"
When you pass pathlog to the unmanaged code it then does the following:
sprintf(path,"%s\\20160212_RFID_Logdatei.dat",path_Logdatei);
which sets path to be
path == "C:\\ProgramData\\test\\20160212_RFID_Logdatei.dat\\20160212_RFID_Logdatei.dat"
In other words you are appending the file name to the path twice instead of once.
An extensive overview for P/Invoke in C# is given in Platform Invoke Tutorial - MSDN Library.
The problematic bit is you need to pass a fixed char array rather than the standard char*. This is covered in Default Marshalling for Strings.
The gist is, you need to construct a char[300] from your C# string and pass that rather than the string.
For this case, two ways are specified:
pass a StringBuilder instead of a string initialized to the specified length and with your data (I omitted non-essential parameters):
[DllImport("MyDLL.dll", ExactSpelling = true)]
private static extern int Magnetfeld_einschalten(
[MarshalAs(UnmanagedType.LPStr)] StringBuilder path_Logfile_RFID);
<...>
StringBuilder sb = new StringBuilder(pathlog,300);
int result = Magnetfeld_einschalten(sb);
In this case, the buffer is modifiable.
define a struct with the required format and manually convert your string to it:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct Char300 {
[MarshalAs(UnmanagedType.ByValTStr,SizeConst=300)]String s;
}
[DllImport("MyDLL.dll")]
private static extern int Magnetfeld_einschalten(Char300 path_Logfile_RFID);
<...>
int result = Magnetfeld_einschalten(new Char300{s=pathlog});
You can define an explicit or implicit cast routine to make this more straightforward.
According to UnmanagedType docs, UnmanagedType.ByValTStr is only valid in structures so it appears to be impossible to get the best of both worlds.
The String is in Unicode format, convert it to byte[]
Encoding ec = Encoding.GetEncoding(System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage);
byte[] bpathlog = ec.GetBytes(pathlog);
and change parameter type to byte[]
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "Magnetfeld_einschalten", CharSet = CharSet.Ansi, ExactSpelling = false)]
private static extern int Magnetfeld_einschalten(byte[] path_Logfile_RFID);
For me it is working
JSh

Issue in Reading/Writing files through P/Invoke in ASP.NET bin folder

I have been trying to write and read files through P/Invoke via my ASP.NET website. I am facing a problem as to where the files are written/read when doing this through dlls in a website. I have tried to explain the problem with the below example:
.cpp file (containing a read and write function)
extern "C" TEST_API int fnTest(char* fileDir)
{
ofstream myfile;
myfile.open (strcat(fileDir, "test.txt"));
myfile << "Writing this to a file.\n";
myfile.close();
}
extern "C" TEST_API char* fnTest1(char* fileDir)
{
ifstream myReadFile;
myReadFile.open(strcat(fileDir, "test1.txt"));
char output[100];
if (myReadFile.is_open()) {
while (!myReadFile.eof()) {
myReadFile >> output;
return output;
}
Post build event of website to copy the dll's from above C++ project to website's bin folder
Default.aspx.cs - C#
Dll functions
public static class Functions(){
DllImport[("Test1.dll", EntryPoint="fnTest", CharSet=CharSet.Ansi]
public static extern int fnTest(string dir);
DllImport[("Test1.dll", EntryPoint="fnTest1", CharSet=CharSet.Ansi]
public static extern StringBuilder fnTest1(string dir);
}
Page_Load event
string direc = AppDomain.CurrentDomain.BaseDirectory + "bin\\";
string txt1 = Functions.fnTest(direc).ToString(); //failing here - keeps on loading the page forever
string txt2 = Functions.fnTest(direc).ToString(); //failing here - keeps on loading the page forever
If I try the same Page_Load code in a desktop application with direc being set as current directory of the project output, everything works fine. It's only that the directories where the files are to be written or read are kind of messed in case of web site and I am not really able to figure out how to correct this and get it working. Suggestions would be greatly appreciated.
You still have a number of the same problems as you have in the last question.
This time round your biggest problem is here:
strcat(fileDir, "test.txt")
You can't modify fileDir since it is owned by the pinvoke marshaller. Instead of passing the directory to your native code, pass the full path to the file. Use Path.Combine in your managed code to create that, and pass it to the native code.
extern "C" TEST_API int fnTest(char* filename)
{
ofstream myfile;
myfile.open(filename);
myfile << "Writing this to a file.\n";
myfile.close();
}
and in managed code
string filename = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory, "bin", "test.txt");
string txt1 = Functions.fnTest(filename).ToString();
In a comment you explain that you need to concatenate strings in the native code. You will need to create a native string to do that because you are not allowed to write to fileDir. Something like this:
string fileName = string(fileDir) + "test.txt";
myfile.open(fileName.c_str());
But you still need to fix fnTest1 which reads the file. My answer at your other question tells you how to do that.

P/Invoke in ASP.NET (Reading/writing text file from dll)

I have a C++ Win32 program in which I am writing and reading a text file. This C++ program generates a dll and I am referencing this dll in my ASP.NET web application.
Using P/Invoke, I am calling methods to read and write file from this dll.
The dll is working fine when I tested this out with P/invoke in WPF application.
The reference dll is in the bin/Debug folder for this WPF app, and the write method in dll when called generates a text file in the same folder.
Further, from the same folder, I can use the dll's read method to read the text file.
However, when I call the Dll methods from my ASP.NET web app, the genearted file goes to some other directory (most probably because the dll is loaded somewhere else to execute) and I am not able to locate where this generated file goes (without any error)
Similar to desktop application, is there some way that the fie will be written in bin folder itself, so that I can read from the bin folder itself?
Example code:
.cpp file
extern "C" D_API int Write1()
{
ofstream myfile;
myfile.open ("example.txt");
myfile << "Writing this to a file.\n";
myfile.close();
return 1;
}
extern "C" D_API char* Read1()
{
ifstream myReadFile;
myReadFile.open("test.txt");
char output[100];
if (myReadFile.is_open())
{
while (!myReadFile.eof())
{
myReadFile >> output;
}
}
return output;
}
C# .aspx.cs
[DllImport("Testing1.dll", EntryPoint = "fnTest", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int Write1();
[DllImport("Testing1.dll", EntryPoint = "ReadTest", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern StringBuilder Read1();
Since you are using relative paths, the file will be relative to the working directory of the process at the point at which you call into the native code. This is a rather brittle arrangement as you have discovered.
I would solve the problem by adding an extra string parameter to the native code that specifies the full path of the file to use. You can generate this easily enough from your managed code I am sure.
Native code
extern "C" D_API int WriteTest(char *filename)
{
....
myfile.open(filename);
....
}
Managed code
[DllImport("Testing1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int WriteTest();
The other point to make is that your function to read data is incorrect. It attempts to return a stack allocated buffer. You need to allocate a buffer in the managed code and then pass that to the native code. Perhaps something like this:
extern "C" D_API int ReadTest(char *filename, char* buffer, int len)
{
//read no more than len characters from filename into buffer
}
And on the managed side:
[DllImport("Testing1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int ReadTest(string filename, StringBuilder buffer, int len);
....
StringBuilder buffer = new StringBuilder(100);
int retval = ReadTest(FullySpecifiedFileName, buffer, buffer.Capacity);

Unable to find an entry point when calling C++ dll in C#

I am trying to learn P/Invoke, so I created a simple dll in C++
KingFucs.h:
namespace KingFuncs
{
class KingFuncs
{
public:
static __declspec(dllexport) int GiveMeNumber(int i);
};
}
KingFuns.cpp:
#include "KingFuncs.h"
#include <stdexcept>
using namespace std;
namespace KingFuncs
{
int KingFuncs::GiveMeNumber(int i)
{
return i;
}
}
So it does compile, then I copied this dll into my WPF's debug folder, with code:
[DllImport("KingFuncDll.dll", EntryPoint = "GiveMeNumber", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int GiveMeNumber(
int i
);
And calling it in button click:
private void Button_Click(object sender, RoutedEventArgs e)
{
int num = GiveMeNumber(123);
}
But it gives me exception:
Unable to find an entry point named 'GiveMeNumber' in DLL
'KingFuncDll.dll'.
Really.... what have I done wrong... It obviously able to find the DLL, otherwise would be another exception. But my method name is exactly the same.... I can't think of other reason.
You need to use extern "C" when you export your function so that you suppress C++ name mangling. And you also should not try to p/invoke to members of a class. Use free functions instead:
extern "C" {
__declspec(dllexport) int GiveMeNumber(int i)
{
return i;
}
}
On the managed side your DllImport attribute is all wrong. Don't use SetLastError which is for Win32 APIs only. Don't bother setting CharSet if there are not text parameters. No need for ExactSpelling. And the calling convention is presumably Cdecl.
[DllImport("KingFuncDll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int GiveMeNumber(int i);
The problem is that you are declaring the C++ "function" inside a C++ class and are telling P/Invoke to use StdCall.
Try to declare a C++ function outside a class and and export it like you did. Then your code should work.
If you really must have a C++ function inside a class, take a look at CallingConvention.ThisCall. But then you are responsible for creating your unmanaged class instance and pass it as the first parameter of your P/Invoke call
The entry point name of a dll file is given in .exp file which is found in debug folder where other source files are present. If dumpbin doesn't work you can try this.

Categories

Resources