|
Okay, so I'm trying to get into using DllImport and it's ilk, in order to write a wrapper for a C++ lib.(libfaac, an aac encoder lib)
I've been reading up on the subject and made some progress but the problem is one function returns a pointer to a struct which contains the settings basically, which the user can change, and you then send the struct pointer back into another function for the changes to take effect. But as far as I understand, the struct will not be shared between the managed/unmanaged code? Is this correct and is there a way to fix this?
|
|
|
|
|
Hi,
if I understand you correctly, you have native code allocating memory (a struct) and returning a pointer to it, then managed code is interested in that data. If so, AFAIK the data will get copied no matter what: managed code will only work on managed data, so either code you provide, or some marshaling code generated automatically, will take care of copying the data across the managed/native border.
When designing an interface, it is therefore better to have the managed world provide the memory and pass an IntPtr to the native world for it to fill; which means you may have to split the native function in two parts, one to calculate and return the required size, and one to provide the actual data.
Of course if the struct is small, having it copied back and forth may not be too bad;
for large arrays etc. (images, sound files, ...) it should be avoided though.
|
|
|
|
|
Thanks for the response!
Maybe I should've mentioned I can't change the native part(Well, can't is a strong contraction, I wouldn't know how is more accurate :P).
The struct is only 32 bytes so not too bad, and I've got it compiling and running now only... The struct contains garbage when I get it from the native part. I did some reading and gathered that member order could affect things, but is there anything else? How can I tell what LayoutKind and Pack parameters are needed for the StructLayout attribute?(If that is even the problem)
EDIT: If I try to use the other function to feed the struct back to the native code after setting sensible values I get another MarshalDirectiveException "Cannot marshal 'parameter #2': Invalid managed/unmanaged type combination (this value type must be paired with Struct)."
modified on Tuesday, January 6, 2009 3:16 PM
|
|
|
|
|
OK,
be careful with element sizes: C/C++ long is 4B (C# long is 8B); C/C++ char often is 1B
(C# char is 2B); pointers are 4B/8B depending on Win32/Win64 (IntPtr should take care of that).
AFAIK order is preserved if all elements are naturally aligned (e.g. each int got preceded by a multiple of 4 data bytes, so there are no gaps to get alignment; if there are gaps, I expect a reordering to obtain minimal total size).
You can use explicit marshaling specification, as in:
[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Ansi)]
public unsafe struct vc1_sBlk {
[FieldOffset(0), MarshalAs(UnmanagedType.I4)]
public int eBlkType;
[FieldOffset(4), MarshalAs(UnmanagedType.AsAny)]
public byte Coded;
...
Obviously it should match the native side, so it all depends on how they coded it,
and what tools and switches got used (read the doc!!).
If you have running native code and can influence the data it generates, you can
experiment (say first accept it as all ints, the managed/native border does not care
about it) and look at the data, then start feeding it the correct information about the struct layout.
|
|
|
|
|
Here's a situation I have:
I have a class Object that has a Property Vector. Vector has a set() function that does a lot of important stuff.
The Vector type itself has three Properties X, Y, and Z. Now, when I write a line of code like:
Object.Vector.X = 10;
I would like the Vector set() function to be called. But it's not because the Vector has not been set to a new Vector, only one of its values has changed.
How can I have a change to one of Vector's Properties call the set() function for Vector itself?
Thanks!
|
|
|
|
|
You simply cant do what you're asking, you're not calling the setter of Vector as you pointed out yourself.
The best solution I can think of is make the vector class raise an event when one of its properties (X,Y,Z) changes, and have Object subscribe to this event so any important work can be done whenever a property of its Vector member is changed.
|
|
|
|
|
In property X setter can't you simply call the method you want. Something like:
public int X {
set {
this.variable = value;
this.set();
}...
|
|
|
|
|
Do you just need the parent object to get notified that the vector has changed? If so, you could add a changed event to your vector, and fire that inside the XYZ properties
|
|
|
|
|
The only way I know of to achieve this is to firstly make Vector a class.
Secondly create a TypeConverter deriving from ExpandableObjectConverter and in that converter override GetCreateInstanceSupported and make it always return true - return true; .
Next, override CreateInstance to return a new Vector - return new Vector((int)propertyValues["X"], (int)propertyValues["Y"], (int)propertyValues["Z"]);
Decorate the Vector class declaration with a TypeConverter attribute referencing the typeconverter you have created.
Lastly in the Vector class decorate each of the public properties (X, Y, Z) with [System.ComponentModel.NotifyParentProperty(true)]
I hope that I have understood your problem and that this helps. Please come back if I have not been clear.
Honi soit qui mal y pongs - Evil to he who thinks it stinks
|
|
|
|
|
Well, I implemented what you suggested (without a clear understanding of what it all does), but it doesn't seem to have solved my problem.
What I'm hoping for is something that will make it so that when the set() function for my X Property returns, it calls the set() function for my Vector Property. Was that the intent of what you gave me? If so, perhaps I have done something wrong. Because it is not currently doing that.
NotifyParentProperty(true) looks very promising. How does that work exactly. Is the TypeConverter necessary for that to work?
|
|
|
|
|
Eric Burns wrote: Is the TypeConverter necessary for that to work
Not sure about that.
I'll go away and dig out some working code. Get back to you shortly.
Honi soit qui mal y pongs - Evil to he who thinks it stinks
|
|
|
|
|
My working examples are complicated and it is taking a while to whittle them down enough to be clear.
Meanwhile, look here[^] MS has an example that has pretty much everything I talked about. Just add the two overrides to the BorderAppearanceConverter.
I'll get back to you when I've finished refactoring.
Honi soit qui mal y pongs - Evil to he who thinks it stinks
|
|
|
|
|
Sorry to have been so long. Had some interesting problems, which I'll tell you about later.
First the working code, as promised.
A simple Vector class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace TestControlLibrary
{
[
Serializable,
TypeConverter(typeof(TestControlLibrary.TCLVectorConverter))
]
public class Vector
{
private int x = 0;
private int y = 0;
private int z = 0;
public Vector(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
[
NotifyParentProperty(true)
]
public int X
{
get
{
return this.x;
}
set
{
if (this.x != value)
{
this.x = value;
}
}
}
[
NotifyParentProperty(true)
]
public int Y
{
get
{
return this.y;
}
set
{
if (this.y != value)
{
this.y = value;
}
}
}
[
NotifyParentProperty(true)
]
public int Z
{
get
{
return this.z;
}
set
{
if (this.z != value)
{
this.z = value;
}
}
}
}
}
The Converter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
namespace TestControlLibrary
{
public class TCLVectorConverter : ExpandableObjectConverter
{
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
{
return new Vector((int)propertyValues["X"], (int)propertyValues["Y"], (int)propertyValues["Z"]);
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
bool result = false;
if (sourceType == typeof(string))
{
result = true;
}
else
{
result = base.CanConvertFrom(context, sourceType);
}
return result;
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
Vector result = null;
if (value is string)
{
try
{
string propertyList = (string)value;
string[] properties = propertyList.Split(',');
result = new TestControlLibrary.Vector(Convert.ToInt32(properties[0]), Convert.ToInt32(properties[1]),
Convert.ToInt32(properties[2]));
}
catch
{
throw new ArgumentException("The arguments are not valid for a Vector.");
}
}
else
{
result = (Vector)base.ConvertFrom(context, culture, value);
}
return result;
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return ((destinationType == typeof(string)) || (destinationType == typeof(InstanceDescriptor)));
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
object result = null;
if (value is TestControlLibrary.Vector)
{
if (destinationType == typeof(string))
{
StringBuilder stringResult = new StringBuilder();
TestControlLibrary.Vector vector = (TestControlLibrary.Vector)value;
stringResult.AppendFormat("{0}, {1}, {2}", vector.X, vector.Y, vector.Z);
result = stringResult.ToString();
}
if (destinationType == typeof(InstanceDescriptor))
{
TestControlLibrary.Vector vector = (TestControlLibrary.Vector)value;
object[] properties = new object[3];
Type[] types = new Type[3];
types[0] = typeof(int);
properties[0] = vector.X;
types[1] = typeof(int);
properties[1] = vector.Y;
types[2] = typeof(int);
properties[2] = vector.Z;
ConstructorInfo ci = typeof(TestControlLibrary.Vector).GetConstructor(types);
result = new InstanceDescriptor(ci, properties);
}
}
else
{
result = base.ConvertTo(context, culture, value, destinationType);
}
return result;
}
}
}
If you build this and place an instance of TestControl on a form you will find that you can set the vector properties, it appears in the Misc category cos I didn't set a Category Attribute, on the one line e.g. 5, 10, 20 OR you can expand the Vector property and set X, Y or Z individually. In exactly the same way that the Location or Size properties can be set. Whichever way you do it the properties are set on the control instantly, because even when you set the individual sub-properties responsibility passes up to Vector to do the setting.
AND NOW FOR SOMETHING COMPLETELY DIFFERENT
The problems I had was with the converter , more specifically with its name. You will notice that it is called TCLVectorConverter in the code above.
When in my earlier message I said I had working code, I was not telling fibs, I really did, but to be super helpful I renamed my component to Vector to match your original post. When I tried to use the code I kept getting errors from the designer that it could not serialize Vector .
It took ages and ages for me to discover the cause was the name VectorConverter . When I renamed it as above it all worked perfectly.
I know why I was getting errors, but do not understand it. There is a Vector class in System.Windows , but that ain't the problem. There is also a VectorConverter class in System.Windows and that IS the problem. Even though I went through the code qualifying all references to Vector and VectorConverter (TestControlLibrary.Vector and TestControlLibrary.VectorConverter ), somehow and in some way there was a clash with the System.Windows version of VectorConverter . Maybe a wiser head than mine can explain it, but I am at a loss.
Once you have working code, you might, just for laughs, rename TCLVectorConverter back to plain VectorConverter to watch the fun.
Never the less working code, as promised.
Ain't coding f%&*ing fun.
Honi soit qui mal y pongs - Evil to he who thinks it stinks
|
|
|
|
|
Here's the code for the control using Vector. I've split it in two because of the length.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace TestControlLibrary
{
[Serializable]
public partial class TestControl : UserControl
{
private Vector vector;
public TestControl()
{
InitializeComponent();
this.vector = new Vector(10, 10, 10);
this.RefreshVectorDisplay();
}
private void RefreshVectorDisplay()
{
this.txtX.Text = this.vector.X.ToString();
this.txtY.Text = this.vector.Y.ToString();
this.txtZ.Text = this.vector.Z.ToString();
}
public Vector Vector
{
get
{
return this.vector;
}
set
{
if (this.vector != value)
{
this.vector = value;
this.RefreshVectorDisplay();
}
}
}
}
}
and its designer code
namespace TestControlLibrary
{
partial class TestControl
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
private void InitializeComponent()
{
this.txtX = new System.Windows.Forms.TextBox();
this.txtY = new System.Windows.Forms.TextBox();
this.txtZ = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.SuspendLayout();
this.txtX.Location = new System.Drawing.Point(35, 30);
this.txtX.Name = "txtX";
this.txtX.Size = new System.Drawing.Size(100, 20);
this.txtX.TabIndex = 0;
this.txtY.Location = new System.Drawing.Point(35, 63);
this.txtY.Name = "txtY";
this.txtY.Size = new System.Drawing.Size(100, 20);
this.txtY.TabIndex = 1;
this.txtZ.Location = new System.Drawing.Point(35, 96);
this.txtZ.Name = "txtZ";
this.txtZ.Size = new System.Drawing.Size(100, 20);
this.txtZ.TabIndex = 2;
this.label1.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(55, 8);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(40, 14);
this.label1.TabIndex = 3;
this.label1.Text = "Vector";
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(14, 33);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(17, 14);
this.label2.TabIndex = 4;
this.label2.Text = "X:";
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 66);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(18, 14);
this.label3.TabIndex = 5;
this.label3.Text = "Y:";
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(12, 99);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(17, 14);
this.label4.TabIndex = 6;
this.label4.Text = "Z:";
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 14F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.MistyRose;
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.txtZ);
this.Controls.Add(this.txtY);
this.Controls.Add(this.txtX);
this.Name = "TestControl";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox txtX;
private System.Windows.Forms.TextBox txtY;
private System.Windows.Forms.TextBox txtZ;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
}
}
Honi soit qui mal y pongs - Evil to he who thinks it stinks
|
|
|
|
|
Thank you very, very much for your help. But unfortunately, this won't call the setter on my parent property, it will only update it in the control display. I think I'll need to use the event-based solution suggested by Pete O'Hanlon, et al.
But! Your work is not for naught! You have taught me something very important that I can apply on other parts of the project. So, while this doesn't solve my initial problem, it solves others. So, thank you very, very much!
|
|
|
|
|
Glad to (sort of) help.
Henry Minute
If you open a can of worms, any viable solution *MUST* involve a larger can.
|
|
|
|
|
This is generally off-topic...but I am curious anyway. Do you really have a class called 'Object'? The only reason I ask is because System.Object is central to every type in all of .NET, and creating an alternative Object class could pose its own problems (wherever the alternative Objects namespace is used)...perhapse is even contributing to your problems?
|
|
|
|
|
No, it's not really called Object. ;o) Thanks for checking me out, though. ;o)
|
|
|
|
|
Ok...good. I had to make sure.
|
|
|
|
|
Well, one way to do this could be to implement INotifyPropertyChanged like so:
public class Vector : INotifyPropertyChanged
{
private int _x;
private int _y;
private int _z;
public int X
{
get
{
return _x;
}
set
{
if (_x != value)
{
_x = value;
Changed("X");
}
}
}
public int Y
{
get
{
return _y;
}
set
{
if (_y != value)
{
_y = value;
Changed("Y");
}
}
}
public int Z
{
get
{
return _z;
}
set
{
if (_z != value)
{
_z = value;
Changed("Z");
}
}
}
private void Set()
{
}
#region INotifyPropertyChanged Members
private PropertyChangedEventHandler _propertyChanged;
public event PropertyChangedEventHandler PropertyChanged
{
add
{
_propertyChanged += value;
}
remove
{
_propertyChanged -= value;
}
}
protected virtual void Changed(string propertyName)
{
Set();
PropertyChangedEventHandler handler = _propertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
} As noted in the Set method, you can rely on the event being raised so that the container can respond to any changes you've introduced as a result of the Set method.
|
|
|
|
|
Hi,
I am trying a sample program...to create C# source code from a simple flow chart prepared in visio(not UML models). Please give me any ideas, to start it or let me know are there any tools available to do my job.
Thanks in advance
By,
sree
sree
|
|
|
|
|
Your application is going to need some heavy work in it's ability to plan, design, and generate code. Most flowcharts simply don't provide enough information to generate code like you want. Also, your app is (I can't believe I'm saying this) going to have to understand English in order to do this.
|
|
|
|
|
Hi,
I am attempting to create a service to watch particular directories for excessively large files being created. I've created a Created event handler with the following code:
void fileSystemWatcher1_Created(object sender, FileSystemEventArgs e)
{
FileInfo fi = new FileInfo(e.FullPath);
if (fi.Length > 524288000)// 500MB = 524288000
{
smtpMessage.Body = "A file exceeding 500 MB has been created at " + e.FullPath.ToUpper();
Client.Send(smtpMessage);
}
}
I drop files one at a time into the watched directory and I'll get an email alert for the first file but nothing for subsequent files? Any help greatly appreciated.
C. Richard Wenger
System Administrator
|
|
|
|
|
How are you initializing the watcher?
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
|
This is what I have setup:
this.fileSystemWatcher1.Path = directoryPath1;
this.fileSystemWatcher1.IncludeSubdirectories = true;
this.fileSystemWatcher1.Filter = "*.*";
this.fileSystemWatcher1.Created += new FileSystemEventHandler(fileSystemWatcher1_Created);
C. Richard Wenger
System Administrator
|
|
|
|
|