|
|
Reflector ( Here[^] ) has a class diagramming add-on ( 100% Reflective Class Diagram Creation Tool[^] ), as well as dependency graph generators and loads of other goodies.
Excellent tool.
Henry Minute
Do not read medical books! You could die of a misprint. - Mark Twain
Girl: (staring) "Why do you need an icy cucumber?"
“I want to report a fraud. The government is lying to us all.”
|
|
|
|
|
Hey Henry, first thanks for the reply...
I know both tools, but they dont help me, I need an software that generates non existing interfaces, so i can apply them on classes that has no implementation.
Ones again, thanks for the reply.
With great code, comes great complexity, so keep it simple stupid...
|
|
|
|
|
I have an unmanaged function with a signature as follows:
func1(unsigned __int16 arg1, unsigned __int16 arg2, unsigned __int16** arg3);
now this is being called in the correctly working unmanaged code as:
{
unsigned __int16 arg1;
unsigned __int16 arg2;
unsigned __int16* arg3;
unsigned int test;
....
test = func1(arg1, arg2, &arg3));
.........
.........
}
I have imported the function(func1()) in the managed c# code as:
[DllImport("SomeDLL.dll", EntryPoint = "func1")]
unsafe public static extern uint func1(UInt32 arg1, UInt32 arg2, out UInt32* arg3);
And I am calling this function in my code as:
{
........
UInt32 arg1;
UInt32 arg2;
UInt32* arg3;
uint test;
...........
test = func1(arg1, arg2, out arg3);
............
/* I am using arg3 value over here*/
if(arg3[0] == null)
{
....
}
}
Now my problem is, in the unmanaged code when I access the arg3[0], it gives me a value, but in the managed code it gives me a pointer, to elaborate:
When I watch arg3 in the watch window in unmanaged code it shows as an array with the arg3 as a pointer and its 0 index value just as a value:
Name Value
- arg3 0x0F031321
arg3 1
in managed code it is:
Name Value
- arg3 0x000000000F031321
*arg3 490799104
notice that in the drop down list above the unmanaged code simply has arg3 in the second line whereas the managed code has *arg3. So my guess is in the latter the first value of the array is a pointer.
Even when I import the function as:
[DllImport("SomeDLL.dll", EntryPoint = "func1")]
unsafe public static extern uint func1(UInt32 arg1, UInt32 arg2, UInt32** arg3);
and call it as:
{
........
UInt32 arg1;
UInt32 arg2;
UInt32* arg3;
uint test;
...........
test = func1(arg1, arg2, &arg3);
............
}
Again, I have the same problem in the managed code.
Can someone please point the errors in my marshaling, and suggest some solutions.
Appreciate all the help.
modified on Monday, September 28, 2009 4:06 PM
|
|
|
|
|
Hi,
two questions:
1. if the unmanaged items are unsigned __int16 , why are you modeling them as UInt32 ???
2. what is the purpose of arg3? it looks like an array, now who is going to allocate it? and who is going to read it and write it? My suggestion will depend on this.
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
1. if the unmanaged items are unsigned __int16, why are you modeling them as UInt32???
I am just following an example where the functions that have been imported and are successfully working are using UInt32 for similar signatures. Does this make a difference though? Since it is going to be implicitly converted.
2. what is the purpose of arg3? it looks like an array, now who is going to allocate it? and who is going to read it and write it? My suggestion will depend on this.
arg3 is going to be allocated by the unmanaged code (I guess so, though it is defined in the managed code, as I showed in the code snippet), it is written by the unmanaged function in the DLL whose code/implementation I do not possess. The managed code that I am writing should be able to access/read the arg3 array data, e.g.; if(arg3[0] == null){...}.
|
|
|
|
|
Hi,
1.
yes it can generate correct results, but it isn't a good habit. And it would go wrong if you did similar things with other type pairs, e.g. float and double. I expect it would also cause problems with arrays.
2.
AFAIK managed code can't accept an unmanaged array simply by applying a couple of * or & symbols.
This is the only way I know would work:
- declare an IntPtr variable
- accept the pointer as a "ref IntPtr" or "out IntPtr"
- create a managed array of correct size
- use Marshal.Copy to copy the data from the IntPtr (=unmanaged data) to the new, managed array
FYI: I very much prefer the approach where the managed code creates an array, passes it (as a pointer) to the unmanaged side, which fills it; that however is not how your unmanaged code works right now.
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
Time to get published, I think!
|
|
|
|
|
My article as of yet wouldn't really help here, as this is a situation I advise against. I suggest objects (and hence arrays) be created on the managed side. I might add my suggestion into the article though.
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
I did as follows:
DllImport("SomeDLL.dll", EntryPoint = "func1")]
unsafe public static extern uint func1(UInt32 arg1, UInt32 arg2, ref IntPtr arg3);
And I am calling this function in my code as:
{
........
UInt32 arg1;
UInt32 arg2;
IntPtr arg3 = IntPtr.Zero; //made arg3 as Intptr
int [] managedArray = new int[1]; //created a managed array
uint test;
...........
test = func1(arg1, arg2, ref arg3);
............
/* I am using arg3 value over here*/
Marshal.Copy(arg3, managedArray, 0, 1);
if(managedArray[0] == null)
{
....
}
}
When I watch the managedArray[0] now, it still shows the values as before, kind of garbage values.
I am just writing a single value in the array (as indicated by the last param of Marshal.Copy because only one value is of interest).
Single indirection with out didn't help either.
modified on Monday, September 28, 2009 4:06 PM
|
|
|
|
|
Hi,
your code seems fine, it is what I would do given your description of the situation at hand.
except for the "I am using arg3 value over here" comment which doesn't fit the following line...
To debug, I would set up a slightly larger experiment, that generates say some 10 to 100 numbers of known value (mostly different), and dump the results to see if anything throws and/or anything gets recognized; I tend to use hex notation for debugging, and logging (i.e. write to console or file, not rely on watch windows).
If that doesn't help, I would allocate a managed array, fill it with numbers 1...N and pass its pointer (using GCHandle) to the native side, then watch the array when it returns.
[ADDED]
BTW: when copying the data from an unmanaged to a managed object/array, you now have two objects; you should not forget to get rid of the unmanaged object somehow (i.e. if the native code did a malloc, you should call a function that performs a free).
[/ADDED]
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
modified on Monday, September 28, 2009 4:03 PM
|
|
|
|
|
your code seems fine, it is what I would do given your description of the situation at hand.
except for the "I am using arg3 value over here" comment which doesn't fit the following line...
I have edited this in my previous message now.
To debug, I would set up a slightly larger experiment, that generates say some 10 to 100 numbers of known value (mostly different), and dump the results to see if anything throws and/or anything gets recognized; I tend to use hex notation for debugging, and logging (i.e. write to console or file, not rely on watch windows).
How do you write these data in hex form to the file. Using System.Diagnostics.Debug? I am new to this environment.
If that doesn't help, I would allocate a managed array, fill it with numbers 1...N and pass its pointer (using GCHandle) to the native side, then watch the array when it returns.
I did try this earlier but without using the GCHandle. This is what I did:
DllImport("SomeDLL.dll", EntryPoint = "func1")]
unsafe public static extern uint func1(UInt32 arg1, UInt32 arg2, ref Uint32[] arg3);
And I am calling this function in my code as:
{
........
UInt32 arg1;
UInt32 arg2;
//IntPtr arg3 = IntPtr.Zero; //made arg3 as Intptr
Uint32 [] managedArray = new Uint32[1]; //created a managed array
uint test;
...........
test = func1(arg1, arg2, ref managedArray);
............
/* I am using arg3 value over here*/
if(managedArray[0] == null)
{
....
}
}
It gives a similar kind of garbage value though.
I will try it with GCHandle, probably that will make it more accessible (or is it the only way to make it accessible?) to the unmanaged code.
Thanks
[ADDED]
BTW: when copying the data from an unmanaged to a managed object/array, you now have two objects; you should not forget to get rid of the unmanaged object somehow (i.e. if the native code did a malloc, you should call a function that performs a free).
There is another function that I have imported from the dll that frees the memory allocated by the unmanaged code:
public static extern uint freeMemory(IntPtr arg);
[/ADDED]
modified on Monday, September 28, 2009 4:36 PM
|
|
|
|
|
Hi,
1.
myInt.ToString("X8") turns an int into an 8-digit hex string;
and File.AppendAllText(filename,text) appends some text to a new or existing file
(there are faster ways with File.AppendText if lots of texts need appended).
2.
There are two explicit ways to get a pointer (to array or some other object); one is with the fixed keyword, the other with GCHandle, like so:
int dim=1000;
int[] numbers=makeIntArray(dim);
GCHandle handle=GCHandle.Alloc(numbers, GCHandleType.Pinned);
int sum=SumArray(handle.AddrOfPinnedObject(), dim);
handle.Free();
[DllImport("NativeC.dll")]
public static extern int SumArray(IntPtr pNumbers, int count);
3.
You do have native code that successfully calls the native lib, you said?
so you can read the calling code and understand exactly how it needs to be called, e.g. who is allocating the array.
If unclear, you could post some of that too.
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
3.You do have native code that successfully calls the native lib, you said?
so you can read the calling code and understand exactly how it needs to be called, e.g. who is allocating the array.If unclear, you could post some of that too.
I have mentioned the native codes dll function signature, and how the native code calls it in my first post, if that's what you mean.
BTW I also found this http://msdn.microsoft.com/en-us/magazine/cc164193.aspx[^] article, with the mentioned PInvoke Interop Assistant application I got the result for this signature:
unsigned int func(unsigned __int16 arg1, unsigned __int16 arg2, unsigned __int16** arg3);
as this:
[System.Runtime.InteropServices.DllImportAttribute("<SomeDLL.dll>", EntryPoint="func")]
public static extern uint func(ushort arg1, ushort arg2, ref System.IntPtr arg3);
Which is exactly what you mentioned earlier. Have you used this application? If you have, what is your opinion about it?
I'll get back to you with the results for points 1 and 2.
Appreciate your suggestions.
|
|
|
|
|
I created an int array of size 1000 and initialized it from 0-999. Then I passed its GCHandle to the dll function. I changed the dll function signature to take the third argument as IntPtr. The array gets written to only in its first two indexes. With a kind of memory location address (not hex) or garbage value in index 0, and 0 in index 1 (everytime).
|
|
|
|
|
akhanal wrote: a kind of memory location address (not hex)
a number never is hex nor decimal, it is just a number; it is only input/output that turns it in to a string, which may be decimal, hex, binary, whatever you choose.
is the number a multiple of 4? if so, and probably is a pointer.
now do you have C code that successfully calls your func1?
can you make that work?
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
Yes I have a c code that successfully calls the func1 in the dll and it is working as desired.
[ADDED]BTW do you know of any books that exclusively handles the interops (focusing on c#) and its shenanigans. Including but not limited to datatype marshaling, callbacks both ways, threading, P/Invoke, etc.[/ADDED]
Thanks
|
|
|
|
|
akhanal wrote: Yes I have a c code
Then show it here, so we can lift some doubts.
akhanal wrote: any books
No. Some articles yes. And I am in the middle of writing my own.
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
Here is the modified code for the function that calls func1(getList()) and other functions from the dll:
void listTheMembers(unsigned __int16 ID1, unsigned __int16 ID2)
{
unsigned __int16* arg3;
int i;
unsigned int test;
printf("\nMembers in list %d on server %d:\n", ID2, ID1);
if((test = getList(ID1, ID2, &arg3)) != 0)
{
printf("Error getting member list for %d: %d\n", ID2, test);
return;
}
if(!arg3[0]) {
printf("No members\n\n");
freeTheAllocatedMemory(arg3);
return;
}
for(i=0; arg3[i]; i++)
{
char* nameOfMember;
if((test = getName(ID1, arg3[i], Member_Username, &nameOfMember)) != 0)
{
printf("There was an error in querying member's username: %d\n", test);
break;
}
printf("%u - %s \n", arg3[i], nameOfMember));
freeTheAllocatedMemory(nameOfMember);
}
printf("\n");
freeTheAllocatedMemory(arg3);
}
The signatures for the called functions are:
unsigned int getList(unsigned __int16 ID1, unsigned __int16 ID2, unsigned __int16** result);
unsigned int freeTheAllocatedMemory(void* ptr);
unsigned int getName(unsigned __int16 ID1, unsigned __int16 MemberID, size_t flag, int* MemberName);
No. Some articles yes. And I am in the middle of writing my own.
That's great, it will be awesome to read one.
modified on Tuesday, September 29, 2009 11:11 AM
|
|
|
|
|
seeing the real function name, and example code using it, I now know the native function is allocating an array and setting its pointer through arg3 (which you later must free again). So you don't need any fixed nor GCHandle and this code of yours[^] should be pretty close. However your int [] managedArray = new int[1] is still wrong, they are 16-bit numbers, so use short!
Check again in the way I indicated here[^].
When in doubt, compare results for a native caller and your managed caller.
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
I am receiving following values in the index 0 of the array, and index 1 is always 0:
504150240
504150256
504150272
504150288
504150880
504150896
504150912
504150928
504150944
504150960
It looks like address values.
|
|
|
|
|
That most probably isn't hex as I see 9 digits (for a 16-bit number???); it takes hex to get maximum probability of seeing a pattern if there is one.
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
This is the actual value and its hex equivalent for the 0th index of the array. I printed these values in a file like this:
System.IO.File.AppendAllText("c:\\projectLog.txt", Array[0].ToString());
System.IO.File.AppendAllText("c:\\projectLog.txt", ":");
System.IO.File.AppendAllText("c:\\projectLog.txt", Array[0].ToString("X8"));
System.IO.File.AppendAllText("c:\\projectLog.txt", Environment.NewLine);
505119136:1E1B81A0
505119552:1E1B8340
505140416:1E1BD4C0
505140432:1E1BD4D0
505140448:1E1BD4E0
505140464:1E1BD4F0
505140480:1E1BD500
505140496:1E1BD510
505140512:1E1BD520
505140528:1E1BD530
505140544:1E1BD540
505140560:1E1BD550
Here is to elaborate (remember I initialized the array[1000] with values 0 to 999):
Index 0: 486734960 : 1D02FC70 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
Index 0: 486734976 : 1D02FC80 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
Index 0: 486734992 : 1D02FC90 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
Index 0: 486735008 : 1D02FCA0 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
Index 0: 486735024 : 1D02FCB0 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
Index 0: 486735040 : 1D02FCC0 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
Index 0: 486735056 : 1D02FCD0 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
Index 0: 486735072 : 1D02FCE0 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
Index 0: 486735088 : 1D02FCF0 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
Index 0: 486735104 : 1D02FD00 , Index 1: 0 : 00000000 , Index 2: 2 : 00000002
modified on Tuesday, September 29, 2009 12:10 PM
|
|
|
|
|
OK, these clearly are pointers, they are multiples of 16, and ascending.
And your C code needs to pass one of them to get a member name.
You still have lots of details wrong, e.g. pointers cannot be int16, yet you declare arg3 as int16 pointer.
FYI: I retire from this thread; it has taken you till now to not tell what it is about, to provide real function names, and to mostly ignore what I have replied before.
You still have a lot to learn, I suggest you read a couple of books first.
Luc Pattyn
Local announcement (Antwerp region): Lange Wapper? Neen!
|
|
|
|
|
You still have lots of details wrong, e.g. pointers cannot be int16, yet you declare arg3 as int16 pointer.
That is not my code, it is part of a propriety SDK that I received to work with. And I am under obligations not to release the code (might get fired ). And that SDK has the int16 implementation.
I retire from this thread
I am sorry to hear that.
it has taken you till now to not tell what it is about, to provide real function names
Well you now know why.
mostly ignore what I have replied before
That's not true I have used almost all of your suggestions, and learned a lot in the process.
You still have a lot to learn, I suggest you read a couple of books first.
That is very true, that is why I was asking you about the particular book (If you have any suggestions in this regard it will be helpful). Thank you very much for all the time and efforts you have put on this. I appreciate it a lot.
|
|
|
|
|