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.
Related
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.
I am currently stuck while trying to call a c# methods from python. I am using python 3.2 and not IronPython. I used pip to install the latest version of python.net
Problem occurs (as often discussed) while using ref or out parameters.
Here is my code so far:
import clr
path = clr.FindAssembly("USB_Adapter_Driver")
clr.AddReference(path)
from USB_Adapter_Driver import USB_Adapter
gpio = USB_Adapter()
version2 = ''
status, version = gpio.version(version2)
print ('status: ' + str(status))
print ('Version: ' + str(version))
readMask = bytearray([1])
writeData = bytearray([0])
print (readMask)
print (writeData)
status, readData = gpio.gpioReadWrite(b'\x01',b'\x00',b'\x00')
status, readData = gpio.gpioReadWrite(readMask[0],writeData[0],b'\x00')
status, readData = gpio.gpioReadWrite(readMask[0],writeData[0],)
I have had some major issues getting clr. running at all. But in this exact config it seems to work (I need to save the path to a variable, otherwise it wont work, I also cant type the path the dll in clr.AddReference(path) because this wont work as well)
The c# version method looks like this:
public USB_Adapter_Driver.USB_Adapter.Status version(ref string ver)
My status variable gets a value which works perfectly with the status enum for the c# class.
Problem is: after the call my variable "version" is empty. Why? According to: How to use a .NET method which modifies in place in Python? this should be a legal way to do things. I also tried to use the explicit version but my namespace clr does not contain clr.Reference().
The next (and more severe) problem is pio.gpioReadWrite().Here the info about this one:
public USB_Adapter_Driver.USB_Adapter.Status gpioReadWrite(byte readMask, byte writeData, ref byte readData)
Here I get the error message:
TypeError: No method matches given arguments
It doesn't matter which of the calls I use from above. All of them fail.
Here is the full output of a debugging run:
d:\[project path]\tests.py(6)<module>()
status: 6
Version:
bytearray(b'\x01')
bytearray(b'\x00')
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
d:\[project path]\tests.py(28)<module>()
status, readData = gpio.gpioReadWrite(readMask[0],writeData[0],)
(Pdb) Traceback (most recent call last):
File "D:\WinPython-64bit-3.4.4.2Qt5\python-3.4.4.amd64\lib\pdb.py", line 1661, in main
pdb._runscript(mainpyfile)
File "D:\WinPython-64bit-3.4.4.2Qt5\python-3.4.4.amd64\lib\pdb.py", line 1542, in _runscript
self.run(statement)
File "D:\WinPython-64bit-3.4.4.2Qt5\python-3.4.4.amd64\lib\bdb.py", line 431, in run
exec(cmd, globals, locals)
File "<string>", line 1, in <module>
File "d:\[project path]\tests.py", line 28, in <module>
status, readData = gpio.gpioReadWrite(readMask[0],writeData[0],)
TypeError: No method matches given arguments
Hope one of you has an idea on how to fix this.
Thanks,
Kevin
Python.Net doesn't handle ref/out parameters like IronPython.
status, readData = gpio.gpioReadWrite(b'\x01',b'\x00',b'\x00') call is not quite correct since Python.Net will not return an updated readData as second result.
You can handle ref parameters using reflection. Check out my answer to similar question here
there is a rough code template for your case:
import clr
clr.AddReference("USB_Adapter_Driver")
import System
import USB_Adapter_Driver
myClassType = System.Type.GetType("USB_Adapter_Driver.USB_Adapter, USB_Adapter_Driver")
method = myClassType.GetMethod("gpioReadWrite")
parameters = System.Array[System.Object]([System.Byte(1),System.Byte(0),System.Byte(0)])
gpio = USB_Adapter_Driver.USB_Adapter()
status = method.Invoke(gpio,parameters)
readData = parameters[2]
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
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 ?
I have a Delphi 7 dll that exports the following function:
function StringTest(var StringOut : pchar) : boolean; stdcall;
begin
GetMem(StringOut, 100);
StrPCopy(StringOut, 'Test output string.');
result := true;
end;
This function is imported in C# as follows:
[DllImport(#"C:\\Test\\DelphiTest.dll")]
public static extern bool StringTest(out string stringOut);
When I call the import from a WPF app it works fine and I see my test string returned in the out parameter. When I attempt the same thing from a site hosted in Cassini it works fine as well. However, when I run that method from a site hosted in IIS7 it fails. If I comment out the GetMem and StrPCopy lines the function returns "true" in IIS. How can I get some string data back into C# from Delphi in a site hosted in IIS?
This is not how 'normal' dll functions return strings. It is unclear in your code who should free the string. Maybe that is the reason .Net doesn't always like it. The caller should allocate enough memory to put the result string in.
function StringTest(const StringOut : pchar; MaxLen: Integer) : Boolean; stdcall;
begin
StrPLCopy(StringOut, 'Test output string.', MaxLen);
result := true;
end;
[DllImport(#"C:\\Test\\DelphiTest.dll", CharSet = CharSet.Ansi)]
public static extern bool StringTest(ref string stringOut, int maxLen);