I'm having trouble with importing strings to VB from c++
I have my structure and export function declared like this in c++:
struct test
{
LPSTR text;
};
extern "C" __declspec(dllexport) test Func()
{
LPSTR text;
text = (LPSTR) "JUST TESTING";
test str;
str.text = text;
return str;
}
I'm importing this Func() function to VB I do "surprisingly" succeed with this text:
<DllImport("dlltest.dll", EntryPoint:="Func", CallingConvention:=CallingConvention.Cdecl)>
Private Shared Function AddFunction() As String
End Function
In this case, AddFunction() returns me the string I described in c++ ("JUST TESTING")
But when I try to define my structure in vb, I'm starting to get problems:
Public Structure test
Public t As String
End Structure
<DllImport("dlltest.dll", EntryPoint:="Func", CallingConvention:=CallingConvention.Cdecl)>
Private Shared Function AddFunction() As test
End Function
In this case, the program throws a Marshal Directive Exception error every time I
call the function AddFunction()
dim A as test
A = AddFunction()
I also tried writing this before defining my test structure
<System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)>
And this before defining Public t as string
_<System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> _
And none of these help. I need to read my string from the structure. I have no problems reading integers the same way in structures. Only these strings. Do you have any ideas?
Related
I have a Java class that contains all my code table like data; e.g. code table is called "Status" and has three values like so:
1 => Good
2 => Bad
3 => Ugly
Simplified it looks like this
public class Codes
{
private static Map<String, Map<String, String>> CODES;
public static void init(List<String> ctList) throws Exception
{
try (Connection con = ...)
{
// the SQL executed has an IN(?) where the ctList defines a list of the tables to load
ResultSet results = prepStmt.execute();
while(results.next())
{
String id = results.getString("TYP");
String code = results.getString("CODE");
String desc = results.getString("DESC");
if(!CODES.containsKey(id))
CODES.put(id, new HashMap<>());
Map<String, String> ct = CODES.get(id);
ct.put(code, code);
ct.put(desc.toUpperCase().trim(), code); // allows searching for code by description
}
}
}
public static String getCodeStr(String table, String key)
{
return CODES.get(table).get(key);
}
}
Now inside my application I frequently refer to this class. Simple example
String test = Codes.getCodeStr("test_table_id", "test_code_value");
When mocking I won't be able to connect to a database. So what must I do to be able to test with this class?
One thing I have considered is collect all the code table data into a file and then initialize from the file based on some switch. I just have to make sure the file is kept up to date...
Any ideas or suggestions are welcome but I do prefer to avoid/limit injection when I can.
I want to pass a xml document from VBA template to a C++ dll. I prepared function in this dll:
extern "C" __declspec(dllexport) int __stdcall ProcessRequest(IXMLDOMDocument* request, IXMLDOMDocument* response);
int __stdcall ProcessRequest(IXMLDOMDocument* request, IXMLDOMDocument* response)
{
IXMLDOMElement* root = NULL;
request->get_documentElement(&root);
BSTR bstrVal = NULL;
root->get_text(&bstrVal);
::MessageBox(NULL, bstrVal, L"lol", MB_OK);
return 0;
}
And I call it from VBA like this:
Public Declare Function ProcessRequest Lib "DllName" Alias "_ProcessRequest#8" (ByRef xml1 As DOMDocument, ByRef xml2 As DOMDocument) As Long
Public Sub ProcessRequestTest()
Dim xml1 As New DOMDocument
Dim xml2 As New DOMDocument
Dim x As Long
xml1.loadXML "<xml>lol</xml>"
x = ProcessRequest(xml1, xml2)
End Sub
Yet, I get violation error on: request->get_documentElement(&root);
Why would that be? Is this not a proper way to pass a DOMDocument? Is there a way, or should I just pass strings, from witch dll would create xml?
You have declared the function as ByVal in C++ but ByRef in the VB declare statement.
To pass an interface ByRef, you need to declare it as IXMLDOMDocument**
E.g. you need this in C++:
extern "C" __declspec(dllexport) int __stdcall ProcessRequest(IXMLDOMDocument** pprequest, IXMLDOMDocument** response);
int __stdcall ProcessRequest(IXMLDOMDocument** request, IXMLDOMDocument** ppresponse)
I'm facing troubles with converting this code ( for using this FFmpeg Wrapper ) to C# since it's the main language of my project .
I tried http://www.developerfusion.com/tools/convert/vb-to-csharp/
but the result code didn't work with me :(
I know it's a newbie request, I'm sorry ;
The Code :
Public WithEvents MediaConverter As New FFLib.Encoder
Private Sub ConOut(ByVal prog As String, ByVal tl As String) Handles MediaConverter.Progress
OperationPrgrss.Value = prog
Application.DoEvents()
End Sub
Private Sub stat(ByVal status) Handles MediaConverter.Status
StatusLbl.Text = status
Application.DoEvents()
End Sub
C# doesn't have a strict equivalent for the Handles keyword; what you need to do is add event handlers yourself in the constructor.
public Form1() {
...
// wire up events
MediaConverter.Progress += ConOut;
MediaConverter.Status += stat;
}
You don't need an equivalent for WithEvents, as that just tells VB there are events to be wired up, and in C# you do that yourself.
The rest is a very straightforward translation. A Sub is basically a function with void return type, ByVal and the Handles clauses can go away, keywords are lowercase, and the rest is just semicolons and braces.
For example,
private void ConOut(String prog, String tl) {
OperationPrgrss.Value = prog;
Application.DoEvents();
}
Today I tried to make my first DLL and my first application which would use a DLL.
The DLL is made in C++ and this is the code I'm calling:
void Graph::findPath_r(Node* pStart, Node* pEnd, std::vector<cell> &road, cell &costMx)
{
//.....
if(pEnd->getParent() != NULL)
{
while(!path.empty())
{
road.push_back(path.top()->getName());
costMx += path.top()->getGCost();
path.pop();
}
return;
}
return;
}
vector <int>tbcway;
int FUNCTION CalculatePath(int Start, int End, int * Array, int &cost)
{
dgraph->findPath_r(xNode[Start].NodeID ,xNode[End].NodeID,tbcway,cost);
dgraph->reset();
std::copy(tbcway.begin(), tbcway.end(), Array);
tbcway.clear();
return 1;
}
and this is how I declared it in VB.net and called it:
Imports System.Runtime.InteropServices
Public Class Form1
<DllImport("RCP.dll")> _
Public Shared Function LOAD_SYSTEM() As Boolean
End Function
<DllImport("RCP.dll")> _
Public Shared Function GetPluginVersion() As Integer
End Function
<DllImport("RCP.dll")> _
Public Shared Function CalculatePath(ByVal StartNode As Integer, ByVal EndNode As Integer, ByRef Array() As Array, ByRef cost As Integer) As Integer
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
LOAD_SYSTEM()
MsgBox(GetPluginVersion().ToString())
Dim path(4096) As Array
Dim movecost As Integer
CalculatePath(1702, 27932, path, movecost)
End Sub
End Class
So, what is wrong with this code?
The error I am getting is:
A call to PInvoke function 'RCP GUI!RCP_GUI.Form1::CalculatePath' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
This is probably a calling convention mismatch.
Try decorating your DllImport with different calling conventions to see which works (my guess is that it should be cdecl).
I'm really struggling with the following pinvoke call. I've tried numerous different ways of doing this but still no joy.
The call runs through, but I get a 'Bad parameter' message back with suggests there is something wrong with the struct, since I've ran through a c++ example of this code and the parameters are all correct.
I'd be so grateful for any help, I've googled myself mad already!
in the c++ header file:
int __stdcall DVSNET_OpenChannel(HANDLE hServer,unsigned long nChannel,DVSNET_CHANNEL_INFO *pChannelInfo,HANDLE *phChannel);
typedef struct tagDVSNET_CHANNEL_INFO
{
unsigned long lStructSize;
unsigned long dwStreamNo;
unsigned long nProtocol;
HWND hWndDisplay;
unsigned long bPlayStart;
unsigned long dwBackFrameCount;
unsigned long dwFlag;
} DVSNET_CHANNEL_INFO;
My definitions for the import:
<DllImport("DVSNETClient.dll")> _
Public Shared Function DVSNET_OpenChannel(ByVal hServer As System.IntPtr, ByVal nChannel As UInteger, ByRef pChannelInfo As IntPtr, ByRef phChannel As IntPtr) As Integer
End Function
<StructLayout(LayoutKind.Sequential)> _
Public Structure tagDVSNET_CHANNEL_INFO
Public lStructSize As UInteger
Public dwStreamNo As UInteger
Public nProtocol As UInteger
Public hWndDisplay As IntPtr
Public bPlayStart As UInteger
Public dwBackFrameCount As UInteger
Public dwFlag As UInteger
End Structure
My Calling code:
Private Sub OpenChannel()
Dim intRet As Integer
Dim ChannelInfo As New tagDVSNET_CHANNEL_INFO
Dim HWD As New System.IntPtr
ChannelInfo.lStructSize = System.Runtime.InteropServices.Marshal.SizeOf(ChannelInfo)
ChannelInfo.nProtocol = 0
ChannelInfo.dwStreamNo = 0
ChannelInfo.dwBackFrameCount = 10
ChannelInfo.hWndDisplay = HWD
ChannelInfo.bPlayStart = 0 ' dont display
'Channelinfo.dwFlag =
' Initialize unmanged memory to hold the struct.
Dim ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(ChannelInfo))
System.Runtime.InteropServices.Marshal.StructureToPtr(ChannelInfo, ptr, True)
Dim nChannel As UInteger = 1
intRet = TotemPoleLib.DVSNET_OpenChannel(hServer, nChannel, ptr, hChannel)
'... snip ...
End Sub
Many thanks in advance!
I don't know VB.Net syntax, but if you understand C# then here are the proper P/Invoke declarations for DVSNET_CHANNEL_INFO and DVSNET_OpenChannel:
[StructLayout(LayoutKind.Sequential)]
struct DVSNET_CHANNEL_INFO
{
uint lStructSize;
uint dwStreamNo;
uint nProtocol;
IntPtr hWndDisplay;
uint bPlayStart;
uint dwBackFrameCount;
uint dwFlag;
}
static class DVSNETClient
{
[DllImport("DVSNETClient.dll")]
public static extern int DVSNET_OpenChannel(
IntPtr hServer,
uint nChannel,
ref DVSNET_CHANNEL_INFO pChannelInfo,
ref IntPtr phChannel
);
}
Your DVSNET_CHANNEL_INFO appears fine, but the 3rd argument of DVSNET_OpenChannel should simply be a ref DVSNET_CHANNEL_INFO; all the marshaling is done for you automatically, no real need for the Marshal class here. The only thing you need to do is initialize ChannelInfo.lStructSize to the value of Marshal.SizeOf(typeof(DVSNET_CHANNEL_INFO)) before calling DVSNET_OpenChannel. It would probably be sensible to do this in a non-default constructor for DVSNET_CHANNEL_INFO, but given that it's a struct, there is of course no way to enforce use of that constructor over the implicit default constructor.
Note that semantically it's possible that pChannelInfo and/or phChannel should be out rather than ref, but it's not possible to tell by the signature of DVSNET_OpenChannel. In any case, using ref will work regardless, though it will be less efficient than using out if out is indeed warranted.