How to pass string between C++ (.so) and C# - c#

I want to pass a string back and forth between C++ (.so) and C# on Linux. However, I can't really find an example of using PInvoke on Linux (most search results of PInvoke are on Windows). Thus, what is the best way to do this on Linux?
I have tried the plain char* solution, but it doesn't work. I also don't know if it is safe to do it this way, since it is marshaling between un-managed and managed code.
C++
//NativeSO
extern "C" const char* IO_String(const char *message)
{
// I know it is "const char*", but I just want
// to express what I want to achieve with this function
message = "I changed a C# string";
return message;
}
C#
[DllImport("NativeSO")] //let's assume the file name & path are correct
static extern string IO_String(string message);
...
public void test()
{
string target = IO_String("hi");
bool isTrue = (target == "I changed a C# string");
print(isTrue); //expect to be true
}
I'm expecting the print(isTrue) to be true in C#, but it is not working. The above code's main purpose is to demonstrate the goals that I'm trying to achieve.

Related

C# DLL in Inno Setup Access Violation

I'm trying to reference a C# DLL in my InnoSetup project. What I need is a simple function with one string parameter and a string return value. But even following the example and trying different kinds of marshaling I always end up in a Access Violation.
This is my C# class:
public class NKToolbox
{
[DllExport("EncryptPassword", CallingConvention.StdCall)]
static string EncryptPassword([MarshalAs(UnmanagedType.LPStr)] string password)
{
File.WriteAllText(#"C:\temp\test.txt", password);
return password.Length.ToString();
}
}
I placed the File.WriteAllText to see if the method is even called. But no. I Use the UnmanagedExports package from Robert Giesecke.
And the Inno Setup Code:
function EncryptPassword(pw: WideString): WideString;
external 'EncryptPassword#files:nktoolbox.dll stdcall';
function InitializeSetup: Boolean;
var
str: WideString;
begin
str := EncryptPassword('sdvadfva');
log(str);
result := False;
end;
On the line str := EncryptPassword('sdvadfva') I get a 'Access violation at address ...... Write of address .....' I'm using Inno Setup 5.5.9 Unicode.
I've tried it with different marshaling statements I've found in other threads, I've tried it with the out keyword, with normal string type and WideString hopeless.
[DllExport("EncryptPassword", CallingConvention.StdCall)]
static string EncryptPassword([MarshalAs(UnmanagedType.LPStr)] string password)
In Delphi code, this maps to:
function EncryptPassword(password: PAnsiChar): PAnsiChar; stdcall;
Note also that the C# code returns a string allocated by a call to CoTaskMemAlloc. Your code is expected to deallocate that buffer by calling CoTaskMemFree.
Your code that imports this function attempts to treat the text as COM BSTR strings. That's just not the case.
Using COM BSTR, aka WideString is a good idea. But be warned that there is likely a mismatch between the C# and Inno assumed ABI for return values. Better to use an out parameter. See Why can a WideString not be used as a function return value for interop?
In your shoes I would declare the C# like so:
[DllExport("EncryptPassword", CallingConvention.StdCall)]
static void EncryptPassword(
[MarshalAs(UnmanagedType.BStr)]
string input
[MarshalAs(UnmanagedType.BStr)]
out string output
)
{
output = ...;
}
And the Inno would be like so:
procedure EncryptPassword(input: WideString; out output: WideString);
external 'EncryptPassword#files:nktoolbox.dll stdcall';
I know nothing of Inno so that part of my answer is somewhat reliant on guesswork.

GetBinaryType from Managed code gives opposite results

I'm finding that when pinvoking GetBinaryType from managed code, I'm getting the opposite result of calling GetBinaryType from native code on the same machine.
I've borrowed the marshalling declaration from elsewhere:
public enum BinaryType : uint
{
SCS_32BIT_BINARY = 0, // A 32-bit Windows-based application
SCS_64BIT_BINARY = 6, // A 64-bit Windows-based application.
SCS_DOS_BINARY = 1, // An MS-DOS – based application
SCS_OS216_BINARY = 5, // A 16-bit OS/2-based application
SCS_PIF_BINARY = 3, // A PIF file that executes an MS-DOS – based application
SCS_POSIX_BINARY = 4, // A POSIX – based application
SCS_WOW_BINARY = 2 // A 16-bit Windows-based application
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetBinaryType(
string lpApplicationName,
out BinaryType dwBinType
);
and then call the function as
bool is64bit = false;
BinaryType binType = BinaryType.SCS_32BIT_BINARY;
// Figure out if it's 32-bit or 64-bit binary
if (GetBinaryType(phpPath, out binType) &&
binType == BinaryType.SCS_64BIT_BINARY)
{
is64bit = true;
}
For 32-bit native binaries, GetBinaryType returns BinaryType.SCS_64BIT_BINARY (6), and for 64-bit native binaries, returns BinaryType.SCS_32BIT_BINARY (0).
To verify, I wrote a native command line tool, and ran it against the same binaries.
PCWSTR rgBinTypes[] = {
L"SCS_32BIT_BINARY", // 0
L"SCS_DOS_BINARY", // 1
L"SCS_WOW_BINARY", // 2
L"SCS_PIF_BINARY", // 3
L"SCS_POSIX_BINARY", // 4
L"SCS_OS216_BINARY", // 5
L"SCS_64BIT_BINARY", // 6
};
int _tmain(int argc, _TCHAR* argv[])
{
DWORD binType;
if (argc < 2)
{
wprintf(L"Usage: %S <binary-path>\n", argv[0]);
goto Cleanup;
}
if (!GetBinaryType(argv[1], &binType))
{
wprintf(L"Error: GetBinaryType failed: %d\n", GetLastError());
goto Cleanup;
}
wprintf(L"Binary type: %d (%s)\n", binType, binType < 7 ? rgBinTypes[binType] : L"<unknown>");
Cleanup:
return 0;
}
The command line tool correctly returns 0 (SCS_32BIT_BINARY) for 32-bit native binaries, and 6 (SCS_64BIT_BINARY) for 64-bit native binaries.
I found one reference to someone else having this same issue, but no answer was provided: https://social.msdn.microsoft.com/Forums/en-US/fc4c1cb4-399a-4636-b3c3-a3b48f0415f8/strange-behavior-of-getbinarytype-in-64bit-windows-server-2008?forum=netfx64bit
Has anyone else run into this issue?
I realize I could just flip the definitions in my Managed enum, but that seems awfully kludgy.
This is a WinAPI bug/developer's oversight. You may find this related question useful to read, and it's top answer may help you find the appropriate workaround,
Use a separate 64 bit process, and some IPC, to retrieve the information.
Use WMI to get the module file name.
Use QueryFullProcessImageName.
I ended up going for a completely different workaround. This answer about PE headers mentions the PE headers among 32 and 64 bit Windows executables. You can completely circumvent the WinAPI checking, and have your target executable checked via reading it in the Binary mode and checking if it matches the PE signature.
Sadly, there isn't much info on the problem online. I remember seeing this problem on some forum, where is was clearly listed as bug, but this was about ~10 years ago. I hope as we discuss this problem, more people become aware of it.

Call a function from C# in Clipper

One of my friend has an application built in Clipper. Now he wants to add some new features to his application, but he does not know how to code for it. I can complete his requirements in a console application in .net. So, I written a function like below in C#:
public static void sendSmsDemo(string MobileNo, string Password)
{
Console.WriteLine("Your Mobile Number is : " + MobileNo + "\n" + "Your Password is : " + Password);
}
I call this function in main method's constructor. And my program works fine.
Now, He wants to call this function from his application developed in Clipper. Is there anybody who knows how to communicate between C# app and Clipper app?
If what you're wanting is to call a C# routine natively from Clipper, you're out of luck.
Another approach may be to recode the Clipper app in Vulcan.NET. Vulcan is a .NET native development environment for XBase.
Otherwise, there may be other techniques, but more details are needed: for example, whether your colleague is using Clipper 5 or Harbour, etc. Some more source from the Clipper side showing what is needed would be helpful.
1º First Step
I create a Class Library in C# and compile options, mark Register for COM interop.
2º The Class sample
Public Class Order
Public Function Total() As Decimal
Return 100
End Function
Public Property Description As String = "Teste"
End Class
3º Test in Harbour
// Now is possible use methods e properties the class
include "minigui.ch"
Function Main()
Local oOrder
//HarbourInvoke the name of my Class Library in C#
oOrder = CreateObject("HarbourInvoke.Order")
MsgInfo(oOrder:Total())
MsgInfo(oOrder:Description())
oOrder:Description := "new test"
MsgInfo(oOrder:Description())
oSuma := nil
Return Nil

Uncomment code programmatically

How can I comment out some comment programmatically?
Think of something like this:-
void releaseA()
{
//ToDo:
}
//string A = "Test";
releaseA();
textbox1.Text = A;
How can I achieve this and implement method releaseA to comment out //string A = "Test";
I searched but still can't find anything.
I think what you really want to do is this:
string a = "";
if (condition)
a = "Test";
textBox1.Text = a;
So for your example of a checkbox and a text box:
string text = "";
if (checkBox.Checked)
text = inputTextBox.Text;
resultTextBox.Text = text;
If you want to comment code before a build of the specified file you can work with compiler switches and specify the compiler switch when building the file in VS or with MSBuild.
You can use something like this:
#ifdef _DEBUG
//string A = "Test";
#else
string A = "Test";
#endif
I believe that comments are ignored by the compiler and won't be resident in the overall exe. So this would technically be impossible, given your current code.
Besides, you'd have no reference to which 'line' said code would be on once its compiled to bytecode.
I don't code in c#, but do a bit in c & Objective C, so i am sure this approach is portable.
if i have a feature i want to be able to conditionally enable at compile time, and potentially disable it at runtime (not the other way around!), i use the following approach.
In a global (prefix) header that all relevant files will see (or a command line switch), define a constant that forces the compiler to physically include the feature's code into the executable. this is effectively a bool constant.
#define acme_support 1
In a common header file (eg settings.h) define a wrapper around this which tells the runtime
code if the feature is available. if it's not available, it's hard coded. if it is available, it's an external bool.
#if acme_support
extern bool runtime_acme_support;
#else
#define runtime_acme_support 0
#endif
in the implementation file associated with "settings.h" (lets call it "settings.c"):
#if acme_support
bool runtime_acme_support = 1;
#endif
Then throughout your project where you want to include code for the feature:
#if acme_support
if (runtime_acme_support) {
/* process the acme widgets for the acme store. */
}
#endif
As you can see, the #if / #endif prevents disabled code being enabled if it was not included at compile time, but you can still "disable" the feature at runtime if certain conditions require that (for example required hardware is not present)
Note that's an #if not a #ifdef, as #ifdef will still be true since '0' is still "defined", whereas #if is a boolean test on the value '0' / '1' which is false/true respectively.

Use C++ Component Object Model in C#

I am trying to build a COM Library in C++, using a C# project for testing. Some methods need to return strings to the caller. On calling these methods from C# I get this: "Access violation reading at location ..."
This is the C++ code from my testproject (apart from all the stuff generated by VS 2010 ATL)
//COMTest.idl
[id(1)] HRESULT Test([out,retval] BSTR* ret);
//Program2.h
STDMETHOD(Test)(BSTR* ret);
//Program2.cpp
STDMETHODIMP CProgram2::Test(BSTR* ret)
{
BSTR tmp = (BSTR)CoTaskMemAlloc(sizeof(wchar_t) * 2);
tmp[0] = L'H';
tmp[1] = L'\0';
*ret = (BSTR)tmp;
return S_OK;
}
In C# I just referenced the DLL from the COM-Tab, turned "Embed Interop Types" off, because it caused errors, and ran this:
static void Main(string[] args)
{
COMTestLib.Program2Class instance = new COMTestLib.Program2Class();
string tmp = instance.Test(); //Where the error occurs
Console.WriteLine(tmp); //This is not reached
Console.Read();
}
The error occurs after leaving the Test-Method. I debugged the C++ code from within my C# project and the values are placed in the correct locations. I do not get the error if I try to return 0 (gives null in C#), even if I still allocate memory like in the example.
I can not make sense of the address, which the access violation complains about. It is neither the address I am allocating nor any other address used in the method. What also seems weird to me is that the CoTaskMemAlloc-Function always returns addresses with the first byte set to zero (0x00XXXXXX), but that might just be a COM thing.
I ran out of ideas and I cant find much information on this (except for basic COM tutorials) anywhere. Can anyone help?
BSTRs require extra memory (to keep track of the string len) so must use SysAllocString() function to allocate BSTRs (or use one of the "smart" BSTR classes).
So your original code should read like:
//Program2.cpp
STDMETHODIMP CProgram2::Test(BSTR* ret)
{
*ret = SysAllocString(L"H");
return S_OK;
}
A good reading about BSTRs: http://blogs.msdn.com/b/ericlippert/archive/2003/09/12/52976.aspx
Check that your COM project and test project are both STA. Check the bitness too. What if you replace BSTR by LPSTR ?

Categories

Resources