|
Marshalling nested structs is not supported in the .NET Framework. Instead, you need to marshal them as IntPtr s using Marshal.StructureToPtr and back using Marshal.PtrToStructure :
[StructLayout(LayoutKind.Sequential)]
public struct FirstStruct
{
[MarshalAs(UnmanagedType.R4)] public double XSpeed;
[MarshalAs(UnmanagedType.SysInt)] public IntPtr DRate;
[MarshalAs(UnmanagedType.SysUInt)] public IntPtr RotCtrl;
public FirstStruct(double XSpeed, long DRate, long RotCtrl)
{
this.XSpeed = XSpeed;
this.DRate = new IntPtr(DRate);
this.RotCtrl = new IntPtr(RotCtrl);
}
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct SecondStruct : IDisposable
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=20)] public string Model;
[MarshalAs(UnmanagedType.SysUInt)] public IntPtr SuptText;
public IntPtr CurSpd;
public IntPtr MaxRdSpd;
[MarshalAs(UnmanagedType.SysUInt)] public IntPtr cNumSpd;
public IntPtr CrwWrd;
public IntPtr DWrd;
public SecondStruct(string Model, long SuptText, FirstStruct CurSpd,
FirstStruct MaxRdSpd, long cNumSpd, FirstStruct[] CrwWrd,
FirstStruct[] DWrd)
{
if (CrwWrd == null || DWrd == null)
throw new ArgumentNullException(CrwWrd == null ? "CrwWrd" : "DWrd");
if (CrwWrd.Length > 40) throw new ArgumentException("Error", "CrwWrd");
if (DWrd.Length > 20) throw new ArgumentException("Error", "DWrd");
this.Model = Model;
this.SuptText = new IntPtr(SuptText);
this.cNumSpd = new IntPtr(cNumSpd);
GCHandle handle = GCHandle.Alloc(CurSpd);
this.CurSpd = handle.AddrOfPinnedObject();
handle = GCHandle.Alloc(MaxRdSpd);
this.MaxRdSpd = handle.AddrOfPinnedObject();
handle = GCHandle.Alloc(CrwWrd);
this.CrwWrd = handle.AddrOfPinnedObject();
handle = GCHandle.Alloc(DWrd);
this.DWrd = handle.AddrOfPinnedObject();
}
void IDisposable.Dispose()
{
GCHandle handle = (GCHandle)this.CurSpd;
if (handle.IsAllocated) handle.Free();
handle = (GCHandle)this.MaxRdSpd;
if (handle.IsAllocated) handle.Free();
handle = (GCHandle)this.CrwWrd;
if (handle.IsAllocated) handle.Free();
handle = (GCHandle)this.DWrd;
if (handle.IsAllocated) handle.Free();
this.Model = null;
GC.SurpressFinalize(this);
}
}
[return: MarshalAs(UnmanagedType.SysUInt)]
public delegate IntPtr MyFunc(ref SecondStruct Info); This should work. If you have problems with it, see http://www.dotnet247.com/247reference/msgs/24/121227.aspx[^] for another way where you actually alloc each struct and struct array member in global memory. Since structs are allocated on the stack, I only pin and get the address of the first member (the array address) since the unmanaged code should easily get the array from the start address. You might want to add additional code to make sure, however, that the arrays are exactly 40 or 20 elements in size.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Heath Stewart wrote:
// Define the callback.[return: MarshalAs(UnmanagedType.SysUInt)]public delegate IntPtr MyFunc(ref SecondStruct Info);
This should work.
Thanks Very much. But I am not fully clear.
__declspec (dllexport) unsigned int CALLBACK MyFunc(SEC_STRUCT * Info);
This is the function exported by the C++ Dll. How should I call this function from my C# client?
[DllImport("MyDll",CallingConvention=CallingConvention.Cdecl)]
public extern static System.UInt32 MyFunc(ref SecondStruct Info) ;
private void button1_Click(object sender, System.EventArgs e)
{
FirstStruct[] first= new FirstStruct[40];
...
SecondStruct info = new SecondStruct(....,first,...);
System.UInt32 uRet= MyFunc(ref info);
MessageBox.Show(info.Model);
...
}
Is this code correct? How to marshal the SecondStruct to IntPtr using Marshal.StructureToPtr and back using Marshal.PtrToStructure ?
Pls Help. Thanks
Vini
|
|
|
|
|
What I did is an alternative to Marshal.StructureToPtr and Marshal.PtrToStructure (along with using Marshal.AllocHGlobal ). Either way should work.
The reason I define MyFunc as a delegate is because the native signature declared it as a CALLBACK . This indicates (though for your circumstances, I may be wrong) that you don't actually call this method, but simply pass it as a callback to some other function.
If you were to declare it as a P/Invoke method, it would look like this:
[DllImport("mydll.dll")]
private static extern uint MyFunc(ref SecondStruct s);
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Thanks.. Couldn't quite get it correct. The structure is passed to the function in dll as reference. If I declare the structure array variable as IntPtr, should I use pointer arithematics to read the value back from the structure array member of the struct?
Tnx
Vini
|
|
|
|
|
More than likely, this is going to be necessary. See the Marshal class documentation, though, for some methods that may ease the process a little.
One more thing you should consider if you can modify the unmanaged code is a thunking layer. Basically, these come in handy when you have marshaling problems such as this. For example, you could define a method with takes all the fields as parameters for the first and second struct and then assembles them internally into the second struct, which it then calls the unmanaged function you are now. Using this approach, you'd be able to easily marshal members of both of the structs.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Sorry about this. I very much appretiate your help . Still a basic doubt:
Heath Stewart wrote:
[StructLayout(LayoutKind.Sequential)]
public struct FirstStruct
{
[MarshalAs(UnmanagedType.R4)] public double XSpeed;
[MarshalAs(UnmanagedType.SysInt)] public IntPtr DRate;
[MarshalAs(UnmanagedType.SysUInt)] public IntPtr RotCtrl;
public FirstStruct(double XSpeed, long DRate, long RotCtrl)
{
this.XSpeed = XSpeed;
this.DRate = new IntPtr(DRate);
this.RotCtrl = new IntPtr(RotCtrl);
}
}
Is there any problem if I declare the structure as
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct FirstStruct
{
public System.Double XSpeed;
public System.Int32 DRate;
public System.UInt32 RotCtrl;
}
If I want to call a function from a dll written in C++ from my c# client, what should I do? Should I create a similar C# structure with managed DataTypes and use that to call the c++ dll function from my C# client, then use it to get the values returned by the Dll
OR
Should I create a similar structure in C# with Unmanaged Type data and then pass it to the function exported by the Dll and use the values returned?
Is the above both declaration one and the same? I have used [MarshalAs(UnmanagedType.ByValTStr, ....)] only in places where the variable declared was char[].
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)] public string Vendor;
When I debug the program, at the function call I get an 'ExecutionEngineException' . Is it the problem which the marshaling of the structure array?
Kindly help.
Vini
|
|
|
|
|
First, in your declaration it is more desirable to use the intrinsic keywords instead of their actual types, like double instead of System.Double . Either way works, but the former makes your code more readable. They are the same thing, after all.
The problem with declaring the last two as int and uint is that a native int and unsigned int are actual processor dependent. So, if this was running on a 64-bit processor, your struct wouldn't marshal correctly. An Int32 (int ) will always be 32 bits, meaning that your field alignment will be incorrect and you'll get invalid values. That's why I declared them as IntPtr and made the parameters long (to allow for large enough numbers, although your values may never be more than 32 bits).
No matter how you declare your structs, the problem is that nested structures can't be marshalled by the CLR. That's why I suggested a thunk. You could declare a native function and P/Invoke that like so:
[DllImport("whatever.dll", CharSet=CharSet.Ansi)]
private static extern IntPtr MyFuncThunk(
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=20)] string Model,
[MarshalAs(UnmanagedType.SysUInt)] IntPtr SuptText,
FirstStruct CurSpd,
FirstStruct MaxRdSpd,
[MarshalAs(UnmanagedType.SysUInt)] IntPtr cNumSpd,
[MarshalAs(UnmanagedType.ByValArray, SizeConst=40)] FirstStruct[] CrwWrd,
[MarshalAs(UnmanagedType.ByValArray, SizeConst=20)] FirstStruct[] DWrd);
[DllImport("whatever.dll")]
private static extern IntPtr MyFunc(IntPtr Info); You pass your data to the thunk which allocs and initializes the struct (SEC_STRUCT ), then returns the address to the struct (a pointer). You can use that pointer then in the call to MyFunc , although I still wonder if you really want to P/Invoke this since the function is most likely not exported, being that it's a CALLBACK . The one way to find out is to load your DLL into depends.exe (comes with the Platform SDK tools, which is installed by default with Visual Studio) and look at the exports.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Thanks. But my function eventhough declared as a call back, is called by the UI. I already have a c++ client which uses this dll and calls the said function. I am trying to build a similar client in C#. As far as writing another method is concerned, I have got only the .dll file and the .h file with me. source code is not available.
I have used the depends.exe, which shows me an entry to the above mentioned function as _MyFunc@4 in the dll. Any idea??
Vini
|
|
|
|
|
In your DllImportAttribute , set EntryPoint="_MyFunc@4" . If you can't modify the source, you'll have to manually marshal the nested structs as I mentioned before by either pinning the first struct or using the appropriate Marshal methods.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Thank You very much...
Finally I think I am getting everything right. I used the method of pinning the first structure , which u mentioned in one of your previous post. Tnx a lot.
cheers
Vini
|
|
|
|
|
Hai Vini Deep,
My need is same as your problem.
Please reply if you have solved it.
Thanks friend.
|
|
|
|
|
To know who was pinging me, I wrote a little program as following:
using System;
using System.Net;
using System.Net.Sockets;
class Class1
{
static void Main(string[] args)
{
Socket sock=new Socket(AddressFamily.InterNetwork,SocketType.Raw,ProtocolType.Icmp);
sock.Bind(new IPEndPoint(IPAddress.Any,0));
byte[] buf=new byte[65535];
EndPoint ep=new IPEndPoint(IPAddress.Any,0);
while(true)
{
int len=sock.ReceiveFrom(buf,ref ep);
if(len<0) continue;
Console.WriteLine("Received {0} byte(s) from {1}",len,((IPEndPoint)ep).Address);
}
sock.Close();
}
}
However, it didn't work at all!
When my friend pinged me from his pc, my pc gave no output!!
When I ran the command "ping 127.0.0.1 -n 1", it output "Received 60 byte(s) from 127.0.0.1", but
in fact it should have output 2 lines--one for icmp echo, the other for icmp echo-reply !!
Why??
It is a bug of the implementation of MS's dotnet framework?
Please help me and I will be quite grateful.
|
|
|
|
|
I have a .tlb file(neteng.tlb) that works on a vb 6 program. This library has module-based methods that i can call anywhere and it works fine.
Next I wrapped this module-based DLL(neteng.tlb) to another VB 6 COM as you have said. I did that by adding a referenced first to "neteng.tlb" library then adding the following code to the class (NETENGAPI is a module inside "neteng.tlb"):
'------------------------------------
Option Explicit
Public Function NE_IsNetwork1(name As String) As Boolean
NE_IsNetwork1 = NETENGAPI.NE_IsNetwork(name)
End Function
Public Function NE_OpenNetwork1(name As String, mode As AccessModeEnum) As Long
NE_OpenNetwork1 = NETENGAPI.NE_OpenNetwork(name, mode)
End Function
Public Sub NE_InitNetworkEngine1()
Call NETENGAPI.NE_InitNetworkEngine
End Sub
Public Sub NE_ExitNetworkEngine1()
Call NETENGAPI.NE_ExitNetworkEngine
End Sub
'------------------------------------
Then save the file as Netengs.DLL (class name is NetEngEng)
===>>> Process Flow: neteng.tlb wrapped inside ->netengs.dll
Note:
Netengs.dll purpose was to call those module-based methods inside neteng.tlb thru the class NetEngEng so we can expose them when we migrate it to .NET assembly code.
I created another VB6 Prog to test netengs.dll if its gonna work (i add a referenced first to netengs.dll). See code below:.
Option Explicit
Dim shp As String
Dim net As NetEngs.NetEngEng
Private Sub Form_Load()
Set net = New NetEngEng
Call net.NE_InitNetworkEngine1
shp = "d:\tracking\shapes\roadcenter_mkt_only.nws"
'Dim net As New NetEngs.NetEngEng
Dim bool As Boolean
'bool = net.NE_IsNetwork1(shp)
bool = net.NE_IsNetwork1(shp)
If bool Then
MsgBox ("Is network")
Else
MsgBox ("Is not network")
End If
Dim ne As Long
ne = net.NE_OpenNetwork1(shp, 1)
If (ne = 0) Then
MsgBox "Network " + shp + " not found.", vbCritical
Exit Sub
End If
Call net.NE_ExitNetworkEngine1
End Sub
'-----------
but when i run the prog and it step into the code Call net.NE_InitNetworkEngine1 it raises this error:
The instruction at "0x00001de2" referenced at "0x00001de2". The memory could not be "read."
My neteng.dll that wraps neteng.tlb don't even work inside another vb 6 prog. Whats wrong with neteng.dll here? Am i missing something? It would be more disaster if i import this to interop assembly. Tnx!
|
|
|
|
|
You have a method trying to access the address of itself, where it won't find any data obviously.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
This is content my my type-library when i open it thru OLE Viewer:
// typelib filename: neteng.tlb
[
uuid(281078B0-B4B9-11D2-92C7-0000F878079B),
version(1.2),
helpstring("Library Neteng"),
helpfile("NetEngine.HLP"),
helpcontext(00000000)
]
library NETENG
{
// TLib : // Forward declare all types defined in this typelib
typedef enum {
NE_READWRITE = 0,
NE_READONLY = 1
} AccessModeEnum;
typedef enum {
NE_SIMPLE = 0,
NE_FASTINDEX = 1
} IndexTypeEnum;
[
dllname("neteng.dll")
]
module NETENGAPI {
[entry(0x60000000), helpstring("Initialize the network engine."), helpcontext(0x0000756f)]
void _stdcall NE_InitNetworkEngine();
[entry(0x60000001), helpstring("Exit from the network engine."), helpcontext(0x00007570)]
void _stdcall NE_ExitNetworkEngine();
[entry(0x60000002), helpstring("Takes in the public attribute record for the Network and creates a Network. This results in a directory whose name is part of the Network attribute record. The Network remains until deleted. A NULL pointer is returned if the Network could not be created."), helpcontext(0x00007573)]
-------------------------------------------------------------
as you can see my neteng.tlb file has a module NETENGAPI but what i don't understand is the inclusion of [dllname("neteng.dll")]. Does it mean that the functionality of the NETENGAPI module resides in another file and that is neteng.dll? Indeed there's another fle associated with it but i can't open it by OLE Viewer it says its not a valid type lib. How i can i use NETENGAPI then in my VB.Net app?Adding reference to neteng.tlb only shows the enumeration members but the module methods can't be called nor recognized(NETENG.NETENGAPI.NE_InitNetworkEngine() is not a member the editor says).
Tnx a lot!
|
|
|
|
|
They aren't methods - they're functions. You need to P/Invoke these from the neteng.dll like so:
[DllImport("neteng.dll")]
private static extern void NE_InitNetworkEngine();
[DllImport("neteng.dll")]
private static extern void NE_ExitNetworkEngine();
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Hi,
I have a panel on which there are some custom objects drawn(For example say ten squares). Know I am about to show a tooltip for each square individually when mouse gets over them. What should I do? Is there any API to show a tooltip in the given X and Y coordinates? Should I create my custom tooltip (for example showing a form with yellow background as a tooltip)?
Any commnets would be greatly appreciated!
abcdabcdabcdabcda
Don't forget, that's Persian Gulf not Arabian gulf!
Murphy: Click Here![^] I'm thirsty like sun, more landless than wind...
|
|
|
|
|
The ToolTip component in the .NET FCL really won't work for this, but there are MANY implementations of tooltips both free here on CodeProject (which you could either use or base your own code from) and commercial solutions available that let you do even more than the ToolTip component in the .NET FCL.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Hi,
Does anyone knows where can I find more informations/discussions of the difference of .Net 1.0 & .Net 1.1.
I've tried to build my project under .Net 1.0, but it has some problems when running under .Net 1.1. So I wnat to know more about this, and taking care to solve the problem.
Thanks for help
|
|
|
|
|
|
Hi guyz and experts,
Got a few questions, I managed to create a windows media web control in a web application. I got a class called MediaPlayer which basically writes the windows media player object to an aspx page. Now, I need to access the windows media player attributes, which I can't because I only write the media player object at runtime in the aspx page. I can only retrieve the URL, because the URL was set by user input after runtime in the code behind class. I need to call a few attributes from the windows media player like the duration, current position, etc, but I can't, I can only set it. Somehow I cannot view the current attributes of the current files in the media player unless I set it first.
Questions:
1. Is there anyway I can treat the media player as a web control and can still access the attributes?
2. Are there any other ways that I can do to achieve such thing? (basically putting the windows media player as an object of a class from the cs file (not writing it as an html object)
Cheers,
|
|
|
|
|
Marveyles wrote:
1. Is there anyway I can treat the media player as a web control and can still access the attributes?
No, not really. The media player is instantiated on the client. If you need to access it on the server, then a separate instance is instantiated on the server (obviously, the two are not the same instance). The best thing to do is also emit client-side script that can access the media player using it's ID that you should also write-out to the page.
Marveyles wrote:
2. Are there any other ways that I can do to achieve such thing? (basically putting the windows media player as an object of a class from the cs file (not writing it as an html object)
And how does the work for the client, then? You seem to be missing the fact that all data from the server is downloaded into the client browser and is separate from the server. Even with HTTP Keep-Alive data that has already been sent to the client is lost to the server - it's basically a streaming protocol.
If you want the player to see the media player (or even to hear music if the media player isn't visible), it has to be output to HTML so the browser can instantiate it. Period.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Question. When making a webpage in c# using asp.net wizard how in the world do you change the path that the adapter points to. For example my database is csharp.mdb and i set the adapter to point to the file that i wanted and tested the connection. all seems fine. When i load the page for testing it trys to reference it from the c:\windows\system32 directory. I tried to find this reference in the code but couldn't any help would be great. I was assuming i could do like i did with my winform application and just put the mdb in the project folder and point to it and all be fine but that didn't work in this case.
I get the error
The Microsoft Jet database engine cannot open the file 'C:\WINDOWS\system32\csharp.mdb'. It is already opened exclusively by another user, or you need permission to view its data.
thanks for the help.
Win32newb
"Making windows programs worse than they already are"
|
|
|
|
|
Store the full path to the MDB file in the Web.config using either the AppSettings section or a custom section. This makes it easy to change.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Heath, thanks for the reply. I haven't much experience with asp.net just trying to make a webpage for the first time using c#. In any event, you mentioned the web.config file. I understand what this file is however I don't really understand what the actually tag if you will is to be in the AppSettings to run the mdb. Could you please post an example for say a database a.mdb that is in the database directory?
Thanks for the help
Win32newb
"Making windows programs worse than they already are"
|
|
|
|
|