|
I'm trying to use WNetEnumResource() in C#. I've imported the functions from the system DLL's, allocated buffer memory etc but there's one little step that's got me beat. My code looks approximately like this (extraneous guff removed).
[StructLayout(LayoutKind.Sequential)]
private struct NETRESOURCE
{
public uint dwScope;
public uint dwType;
public uint dwDisplayType;
public uint dwUsage;
public string lpLocalName;
public string lpRemoteName;
public string lpComment;
public string lpProvider;
};
<p>
[DllImport("Mpr.dll", EntryPoint="WNetEnumResourceW",
SetLastError=true, CallingConvention=CallingConvention.Winapi)]
<p>
unsafe private static extern ErrorCodes WNetEnumResource(
IntPtr hEnum, ref uint lpcCount, byte *buffer, ref uint lpBufferSize);
<p>
private unsafe void EnumerateServers()
{
uint bufferSize = 16384;
byte *buffer = (byte *) Marshal.AllocHGlobal((int) bufferSize);
IntPtr handle = (IntPtr) 0;
ErrorCodes result;
uint cEntries = 1;
<p>
result = WNetOpenEnum(ResourceScope.RESOURCE_GLOBALNET, ResourceType.RESOURCETYPE_ANY, 0, 0, out handle);
<p>
if (result == ErrorCodes.NO_ERROR)
{
aData.Add("WNetOpenEnum succeeded");
<p>
do
{
result = WNetEnumResource(handle, ref cEntries, buffer, ref bufferSize);
<p>
if (result == ErrorCodes.NO_ERROR)
{
NETRESOURCE *p = (NETRESOURCE *) buffer;
}
else if (result != ErrorCodes.ERROR_NO_MORE_ITEMS)
break;
} while (result != ErrorCodes.ERROR_NO_MORE_ITEMS);
}
else
aData.Add("WNetOpenEnum failed");
<p>
Marshal.FreeHGlobal((IntPtr) buffer);
}
As you can see, I've declared the 3rd parameter (the buffer) in the call to WNetEnumResource() function as a byte pointer and used the Marshal.AllocHGlobal to allocate a buffer. Single stepping through the code shows that the initial WNetOpenEnum() succeeds as does the WNetEnumResource() call.
What I can't seem to do (no matter what I try) is to cast the buffer to a pointer to a NETRESOURCE structure. The compiler informs me that I can't take the address or the size of a managed type.
I've probably missed something blindingly obvious but any hints or help would be appreciated.
Rob Manderson
Paul Watson wrote:What sense would you most dislike loosing?
Ian Darling replied.
Telepathy
Then I'd no longer be able to find out everyones dirty little secrets The Lounge, December 4 2003
|
|
|
|
|
And, of course, after posting this I got to thinking and realised I'm bringing a c++ approach to the problem. A bit of spelunking around in System.Runtime.InteropServices revealed the Marshal.PtrToStructure() function which, after changing the NETRESOURCE into a class rather than a struct solves the problem nicely.
Is there a better approach?
Rob Manderson
Paul Watson wrote:What sense would you most dislike loosing?
Ian Darling replied.
Telepathy
Then I'd no longer be able to find out everyones dirty little secrets The Lounge, December 4 2003
|
|
|
|
|
You don't need to use an unsafe context. Instead of using byte* just use ref byte ; ref and out should typically be used for pointers to value types, while they shouldn't be used for reference types - since they're already references - exception when a pointer to a pointer is needed.
Instead of declaring your param as an IntPtr for your struct or enum, you can typically use the ref keyword as well. Take, for example, the typical declaration of SendMessage :
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int handle,
IntPtr wParam, IntPtr lParam); This means that if you want to pass a pointer to a struct (say, for a window message to a common control) that you have to use the method Marshal.StructureToPtr and then pass it. Instead, a much easier - and perfectly ligitimate - alternative would be like so:
[DllImport("user32.dll")]
IntPtr SendMessage(IntPtr hWnd, int msg,
ref MyStruct wParam, IntPtr lParam); All this is done without requiring an unsafe context, which is typically only useful for when you have to perform address references or allocate memory for native calls without using CoTaskMemAlloc or GlobalAlloc (which are available from the Marshal as managed calls).
Keep these things in mind, remember that enums are value types too, and you should find a better, easier alternative.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
You're absolutely right. I iterated my way to a working solution using the unsafe keyword and then, following your advice here, removed it and it still compiles and works.
I kinda feel much like I did 15 or so years ago when I was learning how many *'s to bung in front of a pointer to dereference it Heh I'm still in the early stages of learning C#
My final solution looks like this:
[DllImport("Mpr.dll", EntryPoint="WNetEnumResourceA",
CallingConvention=CallingConvention.Winapi)]
<p>
private static extern ErrorCodes WNetEnumResource(
IntPtr hEnum, ref uint lpcCount, IntPtr buffer,
ref uint lpBufferSize);
I guess I have a little way to go in unlearning the pointer way. Nonetheless, I still had problems with trying to handle the IntPtr buffer parameter. I could see that I was getting back the correct values but could see no way to convert the buffer into a struct (in the sense of being able to use struct members to refer to the various fields).
I changed the struct to a class (thanks to Inside C# by Tom Archer) and wrote this line...
Marshal.PtrToStructure(buffer, p);
where p is a NETRESOURCE instance and now I can reference each value using p. syntax. You can see that I was thinking in c++ terms and trying to cast a pointer to a different type.
Hmmm this works but I can't see how to call the WNetEnumResource function in such a way as to return more than 1 instance of the NETRESOURCE structure. The Marshal.PrtToStructure method gives me no way to step down the buffer to the second instance, or the third, of the NETRESOURCE structure within the buffer given that I cannot take a sizeof or the address of a managed object. I can see why the compiler forbids me this
On the other hand, I can't see how to do it in c++ either This is a port of some code I wrote in c++ and, single stepping through the c++ code, I see that each time (whatever value I set as the count of entries to be returned) it returns 1 entry. This makes sense actually - the MSDN entry for the WNetEnumResource API says that the buffer must be large enough to contain not only the NETRESOURCE structure itself but also the strings it points to (which is why I allocated a very large buffer). I'm guessing that the API fills in the structure values and copies the strings immediately afterward - and if it does that then one can't use standard c++ pointer arithmetic to step down the buffer.
Rob Manderson
Paul Watson wrote:What sense would you most dislike loosing?
Ian Darling replied.
Telepathy
Then I'd no longer be able to find out everyones dirty little secrets The Lounge, December 4 2003
|
|
|
|
|
You actually should stick with a struct to keep it allocated on the stack as opposed to taking the risk that an instance of it will be kept in the heap. Who knows what problems this could cause with the unmanaged code.
There are plenty of ways to convert structs to pointers and pointers to structs (after all, a buffer begins with a pointer and is a certain size). If you review all the methods of the Marshal class, there are several methods to get you exactly what you need, such as Marshal.UnsafeAddrOfPinnedArrayElement , which wraps the unsafe code in a trusted assembly (a base class library assembly) and returns you an IntPtr . If the method takes an address of a struct, you should be able to pass an array of that struct (without a ref since an array is a reference type already). When you get it back, you can either treat the array as-is or use the aforementioned method.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
I have a Panel with AutoScroll set to true and when using the mouse to move the scroll bar the Panel position is updated only when the mouse button is released. How can I make the panel scroll while holding down the mouse button and moving the scroll bar?
|
|
|
|
|
Works fine for me in both .NET 1.0 and 1.1. Are you sure you're using the System.Windows.Forms.Panel from the .NET base class libraries?
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Yes, I am using System.Windows.Forms.Panel but I have not set up any events to specifically react on the scrollbar moving, is that necesssary? The funny thing is that it works fine when using the mouse wheel to scroll just not when clicking on the scroll bar, holding down left mouse button and moving the mouse. Any other ideas to what can cause this?
|
|
|
|
|
There are no events for a such control exposed by classes derived from ScrollableControl . In fact, the only way to know when something is being scrolled is to override WndProc and handy notification window messages the "old fashion way" (if you've done any Win32 programming, I'm sure you'll understand).
What version of .NET are you using? Scrolling should work just fine in any event. I've tried in 1.1 just now (our application at work is still 1.0 and it works correctly) and it works for keyboard and mouse events both (i.e., it continuously scrolls). Any other conditions you think are worth mentioning (like performing owner draw or something)?
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
I have a COM method that requires a GUID to the interface used by a particular Item.
I'm not sure how to get the GUID via c# (I can look it up in shell32 though), or how to convert it to a COM syntax when I have it.
I've checked the net, but there is almost nothing on this subject.
So how do i pass a GUID from C# to COM? C# handles it as a string... is this how I should proceed?
Cheers
Cata
|
|
|
|
|
See the System.Guid structure, which marshals to the GUID structure in C/C++. This even has methods for creating an instance of the Guid struct from a string.
BTW, COM is not a syntax - it's a programming concept. GUIDs can be expressed in many different ways, from a nested array of bytes to a string. Since both are typically in hex form, this is trivial to translate.
And if you're wanting to use a GUID from an attributed interface already declared, you can use Marshal.GenerateGuidForType to get the GUID (or to generate one if a Type is not attributed with the GuidAttribute ).
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
I'm trying to call the following line of code:
Guid myGUID = Marshal.GenerateGuidForType(System.Type.GetType("IShellFolder"));<br />
But I always end up with a null pointer and an exception. The type ISHellFolder is defined as:
<br />
[Guid("000214E6-0000-0000-C000-000000000046"),<br />
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] <br />
interface IShellFolder<br />
{<br />
...<br />
...<br />
}<br />
Not entirely sure why it's not working.
Cata
|
|
|
|
|
The Catalyst wrote:
Guid myGUID = Marshal.GenerateGuidForType(System.Type.GetType("IShellFolder"));
But I always end up with a null pointer and an exception.
Thats becos Type.GetType() requires a fully qualified assembly name, hence returning null and causing your exception.
leppie::AllocCPArticle("Zee blog"); Seen on my Campus BBS: Linux is free...coz no-one wants to pay for it.
|
|
|
|
|
Type.GetType requires a fully-qualified class name (includes namespace) and assembly name if defined in a different assembly (and, optionally, other Type information). That's the first thing wrong with it.
I also mentioned previously in this thread to use typeof when you know the type at design-time.
Guid guid = Marshal.GenerateGuideForType(typeof(IShellFolder)); Did you even look at the documentation for Type.GetType ? You really need to start reading the .NET Framework SDK documentation and pick up a book like I gave you a link for before on COM interoperability with .NET, although this particular question is just a fundamental type issue with the .NET Framework (using C# syntax).
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
I have not got books, but I do have many articles, tutorials, and the SDK which I am working through. While I understand the use of books, I find I have trouble working from them. To be honest, the only thing i've read on programming was the first 40 pages of a Java book. Beyond that i've learned everything myself, C#, HTML etc.
I was unaware of 'typeof', i'm sure i'll use it in the future. However, I still have a problem with my function SHBindToParent.
As for my problem with COM that this question derived from. I wasn't marshaling the GUID as a structure:
[MarshalAs(UnmanagedType.LPStruct)]
Once this was done, It worked fine.
As always your help is much appreciated.
Cata
|
|
|
|
|
|
Is it possible to create an instance of an object that has been passed through as a method parameter with a ref keyword ?
public class BasicClass{...}
public class NewClass: BasicClass{...}
...
Something(ref NewClass)
...
public BasicClass Something(ref BasicClass aclass)
{
return new aclass();
}
Regards, Desmond
|
|
|
|
|
You should look at making a class factory. In particular you should look at Reflection and the Activator.CreateInstance() method.
- Nick Parker My Blog
|
|
|
|
|
Something like
return (BasicClass)Activator.CreateInstance(aclass.GetType()); should work.
Charlie
if(!curlies){ return; }
|
|
|
|
|
Could BasicClass implement ICloneable so that in method Something, you return aclass.Clone() ?
|
|
|
|
|
I'm not going to answer your question since the three people above did, but I did want to comment on your use of the ref keyword: it isn't necessary! An instance of a class is already a reference. There is no need to declare the parameter as a ref parameter and this can actually lead to problems with regard to marshaling.
See Value vs Reference Types in C#[^] (for all of .NET, actually) for more information.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
|
For example, when marshaling to a native function via P/Invoke you actually end up passing a pointer to a pointer.
This also creates memory overhead both in the same AppDomain and when marshaling to different AppDomains.
Just understand that there are reference types and value types. A reference type is just that - a reference. You don't need to ref it unless you need to pass a double-pointer. This is all discussed in the .NET Framework as well and is fundamental in programming for the .NET Framework.
Microsoft MVP, Visual C#
My Articles
|
|
|
|
|
Hi,
I want to create these attributes dynamically in a datagrid from the codebehind.
<asp:datagrid id="dataGrid1" runat="server"
="" bordercolor="black" borderwidth="1" gridlines="Both" cellpadding="3" cellspacing="0" headerstyle-backcolor="#aaaadd">
I'm was thinking of starting like this:
FindControl("DataGrid1")
But there are no members for attributes.
Please help
Yours sincerely
Andla
|
|
|
|
|
First: This should be post on ASP.NET page:
Second:
There are many place to set tyles depend on what style you want to set,these are some hints:
DataGrid.Columns[index].ItemStyle<br />
DataGrid.Columns[index].HeaderStyle<br />
DataGrid.Attributes<br />
DataGrid.CellPadding<br />
DataGrid.CellSpacing
Mazy
"Improvisation is the touchstone of wit." - Molière
|
|
|
|
|