I just spent the last week or so figuring out how to execute C++ code from C# as part of my day job. It took us forever to figure it out, but the final solution is fairly simple.
Now I'm curious... How hard would it be to call Haskell from C#? (Note carefully: That's call Haskell from C#, not the other way around. So the main executable is C#.)
If it's really hard, I won't bother. But if it's reasonably easy, I might have to have a play with it...
Basically, we wrote some C++ code. On Windows it gets compiled into a DLL, on Linux it gets compiled into a shared object (*.so). Then on the C# side you do a DllImport and write some manual memory management code if you're trying to pass anything nontrivial across. (E.g., arrays, strings, etc.)
I know GHC is supposed to support building shared libraries on both platforms, but I'm not sure of the technical details. What's the syntax for exporting stuff, and does the caller have to do anything special to initialise the DLL first?
To be concrete: Suppose there exists a function foobar :: FilePath -> IO Int32. Can somebody throw together a small sketch showing:
What Haskell declarations I need to write to expose this to the outside world.
How do I tell GHC to build a single self-contained DLL / SO file.
Anything special the caller needs to do, beyond the usual process of binding foobar itself.
I'm not too worried about the actual syntax for the C# side; I think I've more or less puzzled that out.
P.S. I did briefly look at hs-dotnet, but this appears to be Windows-specific. (I.e., won't work with Mono, so won't work on Linux.)
As far as both languages are concerned, you can basically pretend you're trying to interface with C code.
This is a complex topic, so rather than try to explain all of it, I will focus on making a simple example that you can build on using the resources linked below.
First, you need to write wrappers for your Haskell functions that use types from the Foreign.C.* modules instead of the usual haskell types. CInt instead of Int, CString instead of String, etc. This is the most complicated step, especially when you have to deal with user-defined types.
You also have to write foreign export declarations for those functions using the ForeignFunctionInterface extension.
{-# LANGUAGE ForeignFunctionInterface #-}
module Foo where
import Foreign.C.String
import Foreign.C.Types
foreign export ccall
foo :: CString -> IO CInt
foo :: CString -> IO CInt
foo c_str = do
str <- peekCString c_str
result <- hs_foo str
return $ fromIntegral result
hs_foo :: String -> IO Int
hs_foo str = do
putStrLn $ "Hello, " ++ str
return (length str + 42)
Then, when compiling, you tell GHC to make a shared library:
$ ghc -O2 --make -no-hs-main -optl '-shared' -o Foo.so Foo.hs
From the C# side, in addition to importing the function you want to call, you also have to import hs_init() and call it to initialize the runtime system before you can call any Haskell functions. You should also call hs_exit() when you're done.
using System;
using System.Runtime.InteropServices;
namespace Foo {
class MainClass {
[DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void hs_init(IntPtr argc, IntPtr argv);
[DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)]
private static extern void hs_exit();
[DllImport("Foo.so", CallingConvention = CallingConvention.Cdecl)]
private static extern int foo(string str);
public static void Main(string[] args) {
Console.WriteLine("Initializing runtime...");
hs_init(IntPtr.Zero, IntPtr.Zero);
try {
Console.WriteLine("Calling to Haskell...");
int result = foo("C#");
Console.WriteLine("Got result: {0}", result);
} finally {
Console.WriteLine("Exiting runtime...");
hs_exit();
}
}
}
}
Now we compile and run:
$ mcs -unsafe Foo.cs
$ LD_LIBRARY_PATH=. mono Foo.exe
Initializing runtime...
Calling to Haskell...
Hello, C#
Got result: 44
Exiting runtime...
It works!
Resources:
GHC User's Guide
HaskellWiki
For reference, I was able to get the following procedure to work under Windows...
{-# LANGUAGE ForeignFunctionInterface #-}
module Fibonacci () where
import Data.Word
import Foreign.C.Types
fibs :: [Word32]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
fibonacci :: Word8 -> Word32
fibonacci n =
if n > 47
then 0
else fibs !! (fromIntegral n)
c_fibonacci :: CUChar -> CUInt
c_fibonacci (CUChar n) = CUInt (fibonacci n)
foreign export ccall c_fibonacci :: CUChar -> CUInt
Compile this with
ghc --make -shared Fibonacci.hs
This produces half a dozen files, one of which is HSdll.dll. I then copied that into a Visual Studio C# project, and did the following:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
public sealed class Fibonacci : IDisposable
{
#region DLL imports
[DllImport("HSdll.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern unsafe void hs_init(IntPtr argc, IntPtr argv);
[DllImport("HSdll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern unsafe void hs_exit();
[DllImport("HSdll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 c_fibonacci(byte i);
#endregion
#region Public interface
public Fibonacci()
{
Console.WriteLine("Initialising DLL...");
unsafe { hs_init(IntPtr.Zero, IntPtr.Zero); }
}
public void Dispose()
{
Console.WriteLine("Shutting down DLL...");
unsafe { hs_exit(); }
}
public UInt32 fibonacci(byte i)
{
Console.WriteLine(string.Format("Calling c_fibonacci({0})...", i));
var result = c_fibonacci(i);
Console.WriteLine(string.Format("Result = {0}", result));
return result;
}
#endregion
}
}
The Console.WriteLine() calls are obviously optional.
I haven't tried running this under Mono / Linux yet, but it's presumably similar.
In summary, it's approximately the same difficulty as getting a C++ DLL to work. (I.e., getting the type signatures to match up and making marshaling work correctly is the hard bit.)
I also had to edit the project settings and select "allow unsafe code".
Related
I am trying to use a Golang library that is compiled as a shared C library in my .NET application. It's compiled with the following command: go build --buildmode=c-shared -o main.dll
My Golang code looks like this:
func DoRequest(request *C.char) *C.char {
// ...
// b = []byte
return (*C.char)(unsafe.Pointer(&b[0]))
}
How can I get this string back into a usable form in the .NET world? I've tried this but I get a panic from Go:
[DllImport("C:\\Users\\GolandProjects\\awesomeProject\\main.dll", EntryPoint = "DoRequest", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPStr)]
extern static string DoRequest(byte[] requestJsonString);
static void Main(string[] args)
{
var result = DoRequest(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new Request
{
// ...
})));
Console.ReadLine();
}
Please start with thoroughly reading the doc on cgo.
Strings in Go are not NUL-terminated, so there's thre ways to gateway a Go string across a C-compatible shared library barrier:
Copy it to a C-style NUL-terminated string—yielding a pointer to the first character of a copy, and passing it.
Pass the pointer to the 1st character of a Go string, and then also explicitly pass the string length along with it.
Teach "the other side" about the native Go's string representation and use it. A string in Go is a (packed) struct containing a pointer and a pointer-sized integer.
I am trying to build a WPF GUI to control IXIA interface which is written in TCL (IXIA is application which control hardware), TCL version is 86.
For this purpose, I have created the following:
C# project which consists of
TCL_API.cs class - contains the tcl DLLs.
IXIA.cs class - Contains all methods to access IXIA additianlly the evalScript method that invoke the TCL_API.cs class which is handle with the TCL commands.
WPF in visual Studio 2013. With calling methods to IXIA.cs.
The problem is that one of TCL commands get an error "can not find channel named "stdout".
As I found out, its probably happens because this TCL command contains puts method inside.
I think puts override can solve this issue but don"t know how to do is as I don"t have entire TCL code but only the DLLs.
See the code:
TclAPI.cs
class TclAPI
{
[DllImport("tcl86.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int Tcl_Init(IntPtr interp);
[DllImport("tcl86.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Tcl_CreateInterp();
[DllImport("tcl86.Dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Tcl_Eval(IntPtr interp, string skript);
[DllImport("tcl86.Dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Tcl_GetObjResult(IntPtr interp);
[DllImport("tcl86.Dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern char* Tcl_GetStringFromObj(IntPtr tclObj, IntPtr length);
}
IXIA.cs constructor:
public IXIA(string ip, string username="admin", string password = "admin")
{
interp = TclAPI.Tcl_CreateInterp();
TclAPI.Tcl_Init(interp);
if (interp == IntPtr.Zero)
{
throw new SystemException("can not ini tialize Tcl interpreter");
}
_ip = ip;
_userName = username;
_password = password;
}
evalScript method in IXIA.cs class:
public int evalScript(string script)
{
int Evalres = TclAPI.Tcl_Eval(interp, script);
return Evalres;
}
Since you're interfacing to a real Tcl interpreter, the problem is that the standard channels (of which stdout is a member) are not being registered. I'm not exactly sure what is wrong, but the problem is either that Tcl's incompletely initialised, or that you've built your overall application in a mode that doesn't support standard channels.
If it is the former, adding a registration for Tcl_FindExecutable (takes a single char*, which can be NULL, and returns void) and calling it once, before any other Tcl API functions will fix it. (That function is a bit mis-named; it really should be called something like Tcl_InitLibrary or something like that.)
[DllImport("tcl86.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern void Tcl_FindExecutable(string argv0);
TclAPI.Tcl_FindExecutable(null);
(See How can I run a static initializer method in C# before the Main() method? for a good way to make that call happen exactly once; this is exactly the sort of code that belongs in a static constructor.)
If the latter, you need to either build your application in a mode that ensures that there's a console available, or use Tcl_SetStdChannel to install a replacement. That's a public API, but not one designed to be easy to call from any languages other than C (or C++ in a pinch) since it involves creating a general Tcl channel instance first via Tcl_CreateChannel and that's a non-trivial API; I think you probably ought to regard it as off-limits as it requires quite a lot of knowledge of Tcl's channel model to use well.
Because of the API complexity involved, I'll not guide you through doing a new channel.
You could try replacing the puts command prior to running the problem script… but as that is used for all file and socket writing, not just writing to the console, you need to be careful. Try evaluating the script below before running the problem script:
rename puts puts_original
proc puts {args} {
if {[llength $args] == 1} {
set args [linsert $args 0 stdout]
} elseif {[llength $args] == 2 && [lindex $args 0] eq "-nonewline"} {
set args [linsert $args 1 stdout]
}
if {[lindex $args end-1] eq "stdout"} {
# Doing nothing here, but might want to log somehow?
} else {
puts_original {*}$args
}
}
This isn't code that I'd recommend normally — it's much better to actually fix the standard channels so that they work, and this is all rather flaky — but sometimes it is the best that can be done. Unless the code also creates sub-interpreters or threads, in which case you're really stuck.
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
What is the best practice to share memory of a struct from a C# program to a C++ win32 DLL?
I've used structs in managed shared memory using Boost between two C++ programs and it worked great. I'm lost on the best way to accomplish this between where the struct gets populated in the C# program and the C++ DLL that is an SNMP subagent.
Here's the C++ DLL:
//==================== Code Excerpt from the main cpp file ======================
#include "stdafx.h"
//================= Here we are setting up the shared memory area =====================
#pragma data_seg (".SHAREDMEMORY")
struct sharedData {
int sharedA;
int sharedB;
};
static sharedData A;
#pragma data_seg()
#pragma comment(linker,"/SECTION:.SHAREDMEMORY,RWS")
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
return TRUE;
}
//=============================================================================================
//====================== Here we are writing wrappers to the shared memory area ===========================
//=You must declare it as an Extern "C" to prevent name mangling. This is absolutely necessary in order to import it into c# =
//=============================================================================================
extern "C" __declspec(dllexport) sharedData __stdcall getMyData()
{
A.sharedA = 1237;
A.sharedB = 31337;
//return gshared_nTest;
return A;
}
extern "C" __declspec(dllexport) void __stdcall setMyData( sharedData buff )
{
A = buff;
}
Here's the calling C# function:
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;
namespace sharedMemTestCS
{
public partial class frmSharedMemTestCS : Form
{
struct sharedData {
int sharedA;
int sharedB;
};
static sharedData A;
//============== here we are importing the methods from the win32 dll into the c# console application =================
[DllImport(#"C:\Documents and Settings\My Documents\Visual Studio 2010\Projects\sharedMemTestCPP\Debug\sharedMemTestCPP.dll")]
public static extern sharedData getMyData();
[DllImport(#"C:\Documents and Settings\My Documents\Visual Studio 2010\Projects\sharedMemTestCPP\Debug\sharedMemTestCPP.dll")]
public static extern void setMyData(int data);
public frmSharedMemTestCS()
{
InitializeComponent();
//============== here i am incrementing the value =================
//== i use a message box so that i can have multiple console applications running at once and it will pause at the messagebox (if i don't click ok)
//== i do this so i can see the values changing in the shared memory.
//MessageBox.Show( getMyData().ToString() );
getMyData();
//txtBoxA.Text = (getMyData().ToString() );
}
private void btnAdd_Click(object sender, EventArgs e)
{
//setMyData( getMyData() + 100 );
//txtBoxA.Text = (getMyData().ToString() );
}
}
}
The error message I get is:
Error 1 Inconsistent accessibility: return type
'sharedMemTestCS.frmSharedMemTestCS.sharedData' is less accessible
than method
'sharedMemTestCS.frmSharedMemTestCS.getMyData()' c:\documents and
settings\mconrad\my documents\visual studio
2010\Projects\sharedMemTestCS\sharedMemTestCS\Form1.cs 23 37 sharedMemTestCS
The best practice for sharing memory would be to use the MemoryMappedFile class in C# and CreateFileMapping/MapViewOfFile in C++.
First thing, you cannot straightway use Boost for data sharing. You need to have some well-defined data-structures that you share between the managed and unmanaged worlds.
You may start here
Well your actual problem is your p/invoke expression is public but your struct is private, which is what the error is telling you. Making your p/invoke expression private or your struct public will resolve the immediate issue.
As for the actual data sharing, I've never tried doing it quite like that so I can't tell you if it will or won't work. All the pieces I've worked with are marshalled back and forth. Looking at your example code, it's quite possible it could work. You'll probably want to either copy the data to a new struct for c# or pin your struct that you get back so the GC won't move it around in memory.
I wave a method in C++ that receives a parameter of the LPCOLESTR type. I'm accessing this method through C#, but I can't make the correct conversion between String and this type.
Let's say the method signinature in C++ is:
void Something(LPCOLESTR str)
In C#, I'm trying to call it (all reference issues to access the method through a DLL have been solved already):
String test = "Hello world";
Something(test);
But with no luck, of course. If anyone can help me, I'd be very glad. Thank you!
Code snippet:
As an example, here's my C++ portion of code, defined in the file MixedCodeCpp.h (CLR Class Library)
#include "windows.h"
#pragma once
using namespace System;
namespace MixedCodeCpp
{
public ref class MyClass
{
public:
HRESULT Something(LPCOLESTR str)
{
return S_OK;
}
};
}
And here's my code in C# (I've added a reference to the C++ project in the C# project, through Visual Studio):
StringBuilder sb = new StringBuilder();
sb.Append("Hello world");
MixedCodeCpp.MyClass cls = new MixedCodeCpp.MyClass();
cls.Something(sb);
The argument will appear as Char* on the C# side. That requires unsafe code, like this:
unsafe static void CallSomething(MyClass obj, string arg) {
IntPtr mem = Marshal.StringToCoTaskMemUni(arg);
try {
obj.Something((Char*)mem);
}
finally {
Marshal.FreeCoTaskMem(mem);
}
}
It makes very little sense to expose the LPCOLESTR to other managed code. This method really should accept a String^ and convert to wchar_t* internally.
Try StringBuilder instead of String thusly:
System.Text.StringBuilder test = new System.Text.StringBuilder ();
test.Append("Hello world");
Something(test);
I've used it that way in pinvoke to Win32 functions that required various string pointers as parameters. Not sure it will work with your API but it's worth a shot. Here's some MSDN info about the process. And here is another.
Here's an arbitrary sample of what your import statement and declaration ought to look like. (To be taken with a grain of salt.)
[DllImport(SomeLib.SomeName, CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Something(StringBuilder pMyString);
StringBuilder str = new StringBuilder(MAX_PATH);
DWORD uSize;
bool b = Something(str);