|
Sorry Heath! I told him to ask for you because of your in depth knowledge of marshalling...
RageInTheMachine9532
|
|
|
|
|
No problem, really. Just sticking up for the other regulars. I don't want anyone to be offended on my account.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Sorry about the direct request... Dave from the VB forum said to ask you.....
So here is what I am trying:
Dim fName As String
Dim t as String
Dim s As IntPtr
s = System.Runtime.InteropServices.Marshal.StringToBSTR(fName)
XRawfile.GetAcquisitionFileName(s)
t = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(s)
This gives me the error on build that:
Value of type 'System.IntPtr' cannot be converted to 'String'.
This is in regard to calling XRawfile.GetAcquisitionFileName(s)
Also, using the object browser, the method is described like this:
Public Overridable Function GetAcquisitionFileName(ByRef pbstrFileName As String) As Integer
Member of: AxXRAWFILELib.AxXRawfile
|
|
|
|
|
sbeausol wrote:
Dim s as String
Dim t as String
Dim s As IntPtr
You can not declare the same variable as one type and then again with the same name as another type.
- Nick Parker My Blog | My Articles
|
|
|
|
|
|
If the function requires a String as the parameter, then an IntPtr wouldn't work (the function declaration would've been nice the first time, but oh well).
The problem is that the String must be marshaled as a BSTR , which has a different structure. Unless the function declaration uses a MarshalAsAttribute specifying UnmanagedType.BStr , then I'm not really sure how you can do this off the top of my head. Does the function declaration have this? You may need to use ildasm.exe to look at the declaration of the function in IL.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
here is what I get for:
ildasm.exe Interop.XRAWFILELib.dll
one of the function returning a string:
.method public hidebysig newslot virtual
instance int32 GetAcquisitionFileName(string& marshal( bstr) pbstrFileName) runtime managed preservesig internalcall
{
.custom instance void [mscorlib]System.Runtime.InteropServices.DispIdAttribute::.ctor(int32) = ( 01 00 3B 00 00 00 00 00 ) // ..;.....
.override XRAWFILELib._DXRawfile::GetAcquisitionFileName
} // end of method XRawfileClass::GetAcquisitionFileName
here is what I get with:
ildasm.exe AxInterop.XRAWFILELib.dll
method public hidebysig newslot virtual
instance int32 GetAcquisitionFileName(string& pbstrFileName) cil managed
{
// Code size 33 (0x21)
.maxstack 3
IL_0000: ldarg.0
IL_0001: ldfld class [Interop.XRAWFILELib]XRAWFILELib._DXRawfile AxXRAWFILELib.AxXRawfile::ocx
IL_0006: brtrue.s IL_0014
IL_0008: ldstr "GetAcquisitionFileName"
IL_000d: ldc.i4.0
IL_000e: newobj instance void [System.Windows.Forms]System.Windows.Forms.AxHost/InvalidActiveXStateException::.ctor(string,
valuetype [System.Windows.Forms]System.Windows.Forms.AxHost/ActiveXInvokeKind)
IL_0013: throw
IL_0014: ldarg.0
IL_0015: ldfld class [Interop.XRAWFILELib]XRAWFILELib._DXRawfile AxXRAWFILELib.AxXRawfile::ocx
IL_001a: ldarg.1
IL_001b: callvirt instance int32 [Interop.XRAWFILELib]XRAWFILELib._DXRawfile::GetAcquisitionFileName(string&)
IL_0020: ret
} // end of method AxXRawfile::GetAcquisitionFileName
|
|
|
|
|
Using c# here the code I have so far:
int i;
int k;
string sname = null;
System.DateTime dateDate = new DateTime ();
String fileName = "C:\\Documents and Settings\\sean\\desktop\\00600.RAW";
i = XRawfile.Open(fileName);
k = XRawfile.SetCurrentController(0,1);
string fName = null;
int l = XRawfile.GetCreationDate(ref dateDate);
int j = XRawfile.GetAcquisitionFileName(ref fName ); //fails with type mismatch
textBox1.Text = k.ToString();
|
|
|
|
|
Okay, so it is marshaling the string as a BSTR . Good. I don't use VB.NET much, but syntax you'd need to pass is ref myString , which is typically unusual since a string is already a reference type (though immutable), so you don't usually use ref or out . Unfortunately, I don't know of any equivalent keyword to pass a reference to an argument in VB.NET. You may have to use C#, which has support for better memory manipulation. If you want to have just one assembly, you can compile the C# code as a module (not assemly or EXE) and then compile that into the VB assembly using the /addmodule switch on vbc.exe, the VB.NET command-line compiler.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
I have no problem using C#....
what would my call look like?
|
|
|
|
|
string s = "blah blah blah";
obj.GetAcquisitionFileName(ref s); You could also try using out if ref doesn't work.
Glad to see a VB.NET developer crossing over to the light side...even if only temporary!
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
My C# code:
int i;
int k;
System.DateTime dateDate = new DateTime ();
String fileName = "C:\\Documents and Settings\\sean\\desktop\\00600.RAW";
i = XRawfile.Open(fileName);
k = XRawfile.SetCurrentController(0,1);
string fName = null;
//IntPtr pbString = new IntPtr();
//pbString = Marshal.StringToBSTR(fName);
//fName = pbString.ToString();
int l = XRawfile.GetCreationDate(ref dateDate);
int j = XRawfile.GetAcquisitionFileName(ref fName ); \\fails with type mismatch
textBox1.Text = k.ToString();
|
|
|
|
|
I don't normally do this, but why don't you send me the two interop assemblies using my email address that you can grab off the notificatio message for this post. I'm guessing this is a compile-time error, so I don't need the actual COM dll and you won't loose any trade secrets or anything.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
done
Thanks Again for any help you can offer
|
|
|
|
|
Is nothing saved, or just not the changes?
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
i found the solution, nothing was saved. The problem was that the dataset was not declared public
Stupid mistake
|
|
|
|
|
The DataSet doesn't have to be declared public if its in the same class as the code that's trying to access it. It can be private. Calling methods on the object is not affected by its access modifiers, only accessing the object is dictated by its access modifiers. So, if your button1_Click event handlers was in a different class and you tried to access a privately declared ds from another class, that would be a problem.
Access modifiers only have to do which whether or not objects can be access just within a class (private), by derivative classes (protected), by all classes (public), by classes defined in the same assembly (internal), and either derivative classes or classes defined in the same assembly (protected internal).
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Hi,
Sorry for bringing this again, but I'm having a little problem with C# and Interop.
I want to use mciSendCommand function and a few structures associated with it in C#. I'm declaring this:
<br />
private static UInt32 MCI_OPEN = 0x0803;<br />
private static UInt32 MCI_CLOSE = 0x0804;<br />
<br />
private static ulong MCI_OPEN_SHAREABLE = 0x00000100L;<br />
private static ulong MCI_OPEN_ELEMENT = 0x00000200L;<br />
private static ulong MCI_OPEN_ALIAS = 0x00000400L;<br />
private static ulong MCI_OPEN_ELEMENT_ID = 0x00000800L;<br />
private static ulong MCI_OPEN_TYPE_ID = 0x00001000L;<br />
private static ulong MCI_OPEN_TYPE = 0x00002000L;<br />
<br />
[StructLayout(LayoutKind.Sequential)]<br />
internal class MCI_OPEN_PARMS<br />
{<br />
public UInt32 dwCallback; <br />
public UInt32 wDeviceID; <br />
[MarshalAs(UnmanagedType.LPStr)]<br />
public String lpstrDeviceType;<br />
[MarshalAs(UnmanagedType.LPStr)]<br />
public String lpstrElementName; <br />
[MarshalAs(UnmanagedType.LPStr)]<br />
public String lpstrAlias; <br />
}<br />
<br />
[DllImportAttribute("Winmm.dll")]<br />
protected extern static UInt32 mciSendCommand(UInt32 IDDevice, UInt32 uMsg, ulong fdwCommand, [Out] IntPtr dwParam);<br />
And the I use that like this:
<br />
MCI_OPEN_PARMS openParms = new MCI_OPEN_PARMS();<br />
<br />
openParms.lpstrDeviceType = "cdaudio";<br />
openParms.lpstrElementName = "F:\"<br />
<br />
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(openParms));<br />
Marshal.StructureToPtr(openParms, ptr, false);<br />
<br />
error = mciSendCommand(0, MCI_OPEN, MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, ptr);<br />
When mciSendCommand executes one exception is thrown with the following message: "Object Reference not set to an instance of an object."
I think the problem is in ptr variable, maybe because it's where the function returns it's data.
What am I doing wrong?
I'm sorry for this long topic.
Thanks,
Pedro
|
|
|
|
|
DrGreen wrote:
[DllImportAttribute("Winmm.dll")]
protected extern static UInt32 mciSendCommand(UInt32 IDDevice, UInt32 uMsg, ulong fdwCommand, [Out] IntPtr dwParam);
And the I use that like this:
MCI_OPEN_PARMS openParms = new MCI_OPEN_PARMS();
openParms.lpstrDeviceType = "cdaudio";
openParms.lpstrElementName = "F:\"
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(openParms));
Marshal.StructureToPtr(openParms, ptr, false);
error = mciSendCommand(0, MCI_OPEN, MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, ptr);
The first thing I noticed is that you never defined 'ptr', at least in the code sample you provided. I think you need to add a one little line to the code you posted:
MCI_OPEN_PARMS openParms = new MCI_OPEN_PARMS();
IntPtr ptr;
openParms.lpstrDeviceType = "cdaudio";
openParms.lpstrElementName = "F:\"
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(openParms));
Marshal.StructureToPtr(openParms, ptr, false);
error = mciSendCommand(0, MCI_OPEN, MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, ptr);
RageInTheMachine9532
|
|
|
|
|
Hi Dave, thanks for your awnser.
I define ptr just before I assign the value returned from Marshal.AllocHGlobal to it: IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(openParms));
I put it there to save some lines in my message.
I also tried to Marshal back the ptr into another instance of MCI_OPEN_PARMS to check if the mashal was going wrong, but no, after I do:
<br />
MCI_OPEN_PARMS temp = new MCI_OPEN_PARMS();<br />
Marshal.PtrToStructure(ptr, temp)<br />
I got the same values in temp , so there is no problem with the marshal (I think... )
Any idea?
Thanks
|
|
|
|
|
Missed that! I guess that's what I get for trying to write code while drugged up...
In that case, I can't see what's going wrong with the code. Sorry!
RageInTheMachine9532
|
|
|
|
|
Try passing true to Marshal.STructureToPtr to make sure any possible existing structure is destroyed, otherwise this leads to memory leaks.
Also, a few things about CLS-compliance. Don't use unsigned types if you hope to port your code ever. You can use the signed types and marshal them as unsigned types, using UnmanagedType.U4 for an Int32 , for example. Yo also don't need to use "Attribute" when attributing members - it's implied.
Other than that, there's nothing else that could be null except your string members in MCI_OPEN_PARAMS , the last of which is optional according to the documentation. Everything else besides the IntPtr is a value type, which can't be null .
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Hello Heath,
I've already tried to do do that (pass true to Marshal.STructureToPtr ).
Unfortunately, the problem persists, and when I pass true , the program crashes when it ends (JIT Dialog appears). It's strange...
I'm start to thinking the problem is in those Marshal functions.
|
|
|
|
|
Nope. I use them all the time. Interop in .NET is what I focus on primarily, just see my article on Java/.NET interop without using web services or remoting, right in the presentation layer.
So true is bad in this case (it would be necessary for repeat calls using the same allocated memory).
One thing, the DWORD_PTR should be an IntPtr , which gets marshalled as a UnmanagedType.SysInt automatically by the CLR. A DWORD_PTR uses the same bit-length as an integer on the target platform, i.e. 32-bits on a 32-bit processor, 64-bits on a 64-bit processor, etc. The rest are fine, but you should consider using CharSet=CharSet.Ansi in the StructureLayoutAttribute instead of marshalling the individual fields. Just makes it much easier to read.
Also, a lot of your consts are defined as ulong (aka UInt64 ). When you see LONG or ULONG in native API documentation, it's still a 32-bit integer. You might be getting some overflow problems.
Finally, your mciSendCommand should look like this:
[DllImport("winmm.dll")]
[return:MarshalAs(UnmanagedType.U4)]
private static extern int mciSendCommand(
[MarshalAs(UnmanagedType.U4)] int deviceID,
[MarshalAs(UnmanagedType.U4)] int msg,
[MarshalAs(UnmanagedType.U4)] int command,
IntPtr param); If you'll always be using this to send the MCI_OPEN_PARAMS structure, you can declare it as the following, or add the following as an overload:
[DllImport("winmm.dll")]
[return:MarshalAs(UnmanagedType.U4)]
private static extern int mciSendCommand(
[MarshalAs(UnmanagedType.U4)] int deviceID,
[MarshalAs(UnmanagedType.U4)] int msg,
[MarshalAs(UnmanagedType.U4)] int command,
ref MCI_OPEN_PARAMS);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct MCI_OPEN_PARAMS
{
public IntPtr callback;
[MarshalAs(UnmanagedType.U4)] public int deviceID;
public string deviceType;
public string elementName;
public string alias;
}
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
The problem was in ulong data types, I've change them and now it works fine.
Thanks for your help.
Pedro
|
|
|
|
|