|
hallo,
Given a Font and a string, is there any ready-to-use function in .NET which can compute the resulting width of the string in pixels? So that I can adapt say a Label, MenuItem or Textbox to the new size of a string.
thx, Steve
| | MigrationUser 1 Monday, February 28, 2005 5:45 AM | You can use the Graphics.MeasureString method for that:
<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDrawingGraphicsClassMeasureStringTopic.asp">Graphics.MeasureString</a> | | MigrationUser 1 Tuesday, March 01, 2005 4:43 AM | thanks!
I have seen this function in the help long time ago, but I just couldn't remember the d^mn name & location and found nothing with google. But I knew it existed :-) | | MigrationUser 1 Tuesday, March 01, 2005 10:09 AM | That's true, but bear in mind that the base System.Drawing.Graphics object doesn't expose this method. You need to get a Graphics object from the object you want to manipulate by calling its CreateGraphics method.
Here's an example of resizing a label based on it's own text and font:
Label1.Width = Label1.CreateGraphics.MeasureString(Label1.Text, Label1.Font).Width Label1.Height = Label1.CreateGraphics.MeasureString(Label1.Text, Label1.Font).Height
| | MigrationUser 1 Tuesday, March 01, 2005 10:49 AM | That's also true, but bear in mind that you should always dipose of Graphics objects retrieved with CreateGraphics. Using CreateGraphics inline will leak resources. Dim gfx as Graphics = Label1.CreateGraphics() Dim labelSize as SizeF = gfx.MeasureString(Label1.Text, Label1.Font)
Label1.Width = labelSize.Width Label1.Height = labelSize.Height
<b>gfx.Dispose()</b> | | MigrationUser 1 Tuesday, March 01, 2005 11:42 AM | Really? Doesn't the Dispose method get called implicitly when you create an object without a variable to hold it? I thought that was how the framework handled it...
I just added the following routine to a Button.Click event of an existing testing app:
For i As Integer = 1 To 100000 Label1.Width = Label1.CreateGraphics.MeasureString(Label1.Text, Label1.Font).Width Label1.Height = Label1.CreateGraphics.MeasureString(Label1.Text, Label1.Font).Height Label2.Text = i Application.DoEvents() Next
I started the app and windows task manager and watched the memory usage of the program. The app started with about 30,500K in use. I clicked the button once and ran that routine. The memory usage varied between 33,500K and 35,000K. When the routine stoped, the usage stabilized at about 33M. I could use various other controls on the form and the memory usage would climb and fall. I repeated this process two more times. In the end, the app was still consuming about 33M of memory.
So I didn't really see a sign of a memory leak... Are you sure the explicit call to Dispose is necessary? | | MigrationUser 1 Tuesday, March 01, 2005 2:52 PM | Well, it's not a memory leak you're worried about with Graphics objects. You're getting a handle to the DC that the Grashics object represents, so that's what is getting leaked.
I've never heard that the CLR calls Dispose on objects without a variable, not that I'm an expert on the inner workings of the .NET framework.
The resources will get reclaimed eventually, whether it's by calling Dispose explicitly or the CLR calling the finalizer. If the CLR calls the finalizer, though, it will take two runs of the garbage collector to actually reclaim the object because it first has to be placed in the finalization queue before it actually is destroyed.
It's probably one of those things that, in practice, you'll not notice any difference. I just try to make a habit of explicitly calling dispose on any object that implements IDisposable. | | MigrationUser 1 Tuesday, March 01, 2005 6:51 PM | Well I usually always call .Dispose when a Graphics object is no longer needed.
In this case I have a Graphics object that lives as long as the program runs anyway (unless the window gets resized, then I create a new one, for I thought the old one could be no longer valid, since at least clip bounds have changed...)
| | MigrationUser 1 Wednesday, March 02, 2005 1:53 AM | Ok, this is really an interesting discussion...
Now, I've never specifically heard it said that the CLR calls Dispose on objects without a variable; but to me, it just makes sense given the way inline code execution works. The object must be created in order for the line to execute, but since we haven't allocated any space to hold that object, it must be destroyed when the CLR is finished executing the line of code that created the object. If it didn't clean up after itself you wouldn't be able to do inline execution; every inline call would allocate resources that were never reclaimed (actually, it seems like there were some issues with this in Framework 1.0 using certain calls).
I just did a little research on this, and found the article "Resource Leaks: Detecting, Locating, and Repairing Your Leaky GDI Code" at http://msdn.microsoft.com/msdnmag/issues/01/03/leaks/default.aspx.
In this article, the author states the following:
"A good solution for detecting GDI leaks is to use dedicated tools such as the Purify or BoundsChecker tools I mentioned earlier. The main drawback of these tools is the same as the final MFC dump for memory leaks—you don't know about the leaks until the application ends. It would be nice to be able to take a snapshot of current GDI consumption, perform an action that might leak, and make a comparison with the current state. Under Windows 2000 you can use the Task Manager to do this kind of comparison, thanks to a new column that displays the count of GDI objects used by a process..."
I repeated the experiment in my previous post, this time looking at the GDI Objects column of the TaskManager. My application opened with 44 objects. When the button is clicked and the loop begins to execute, the GDI object count bounces around between 44 and 45, ocassionally hitting 46, and ending at 44 when the loop finishes. This confirms that the Graphics objects are implicitly disposed.
However, the fact that I can occasionally see the count hit 46 indicates that you are correct about it taking an extra run of garbage collection to reclaim the object. A value of 46 indicates that the first inline call to CreateGraphics was not completely disposed before the second call.
You gotta love this forum when it comes to learning more about .NET! Now I know how to look for GDI leaks in my apps; and it was a direct result of this discussion. =) | | MigrationUser 1 Wednesday, March 02, 2005 11:30 AM | I guess I'll have to plead ignorance on this. I come up with pretty much the same results as you when I try to find any leaked resources caused by not calling Dispose on Graphics objects.
I'm guessing that CreateGraphics is repeatedly handing out the same Graphics object and that's why the handle count doesn't grow. If that's the case, GetGraphics might be a better name.
You can varify that Dispose isn't called automatically, though. Here's what I did to test it. First, there's a very simple class that just implements IDisposable and writes to the console when it is disposed or finalized. The Shared function is there so I can get a reference to an instance without using a variable.Public Class TestDispose Implements IDisposable
Public Description As String
Private Sub New(ByVal description As String) Me.Description = description End Sub
Public Shared Function GetObject(ByVal description As String) As TestDispose Return New TestDispose(description) End Function
Public Sub Dispose() Implements System.IDisposable.Dispose Console.WriteLine("**{0} disposed**", Me.Description) GC.SuppressFinalize(Me) End Sub
Protected Overrides Sub Finalize() Console.WriteLine("**{0} finalized**", Me.Description) End Sub
End Class Then I created three buttons on a form and wrote the following: Private Sub btnGo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGo.Click
TestDispose.GetObject("No dispose")
TestDispose.GetObject("Explicit dispose").Dispose() End Sub
Private Sub btnGCCollect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGCCollect.Click Console.WriteLine("Explicit garbage collection...") GC.Collect() End Sub
Private Sub btnCauseGCCollect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCauseGCCollect.Click Console.WriteLine("Inducing garbage collection...") For i As Integer = 0 To 100000 Dim o As New Object Next End Sub When btnGo is clicked, this is written to the console:**Explicit dispose disposed**The only cases "**No dispose finalized**" is written to the console are
1) btnGCCollect is clicked, which explicitly calls GC.Collect 2) btnCauseGCCollect is clicked, which creates a large number of objects, forcing a garbage collection 3) the application terminates.
At no time does **No dispose disposed** ever appear on the console. I guess it's possible that there are some special considerations for BCL objects like the Graphics class, but it's clear that dispose doesn't run unless explicitly called. This is one of those cases where it would be very helpful to have access to the source code to see how it works internally.
In any case, you're right that there doesn't seem to be any measureable difference between calling dispose on the Graphics object retrieved with CreateGraphics and not calling dispose at all.
So, it may have been all for naught, but at least it was interesting :)
| | MigrationUser 1 Wednesday, March 02, 2005 1:36 PM | Like I said, you gotta love these forums for the brainstorming!
To clairify, the handle count DOES grow, it just shrinks again quickly. The number of GDI objects varied by 2 throughout the execution of the loop.
You're right, a view of the source code is the only way to get a real answer, but still, you're example makes me think that even though the actual method Dispose() is never called, the code that Dispose() executes may still be run...
The CLR may have a more generic teardown procedure so that it can execute the same code on IDisposable objects as those without the implementation. This is just speculation but it would make sense in that you could create a class that doesn't implement IDisposable, but still contains a method that returns an object. If you executed this method inline, the object created would still need a teardown. It would make sense for the CLR to have a teardown routine that can be run against any object, regardless of implementation.
This is definately a worthwhile discussion... if nothing else, it should ease minds about the use of inline coding. It has certainly reassured me! =) | | MigrationUser 1 Wednesday, March 02, 2005 2:30 PM | Ok, you'll have to take my word that I haven't spent all day thinking about this :)
I wasn't planning to post on this thread again, but I happened to stop by <a href="http://msdn.microsoft.com">MSDN</a> and noticed a new <a href="http://msdn.microsoft.com/msdntv/episode.aspx?xml=episodes/en/20050217CLRPS/manifest.xml">video</a> about the CLR Profiler. I've read about the tool before, but I'd never had a chance to use it. Anyway, I decided to see if it would help in figuring this out.
I still can't find any <i>direct</i> evidence of leaked GDI resources, which was my original point (the profiler doesn't track them), but the profiler provides plenty of evidence that not calling dispose causes lots of bad mojo. To test it, I wrote two versions of a console application. Here's the meat of both of them:// Version 1 - Dispose not called Label label1 = new Label(); for(int i = 0; i < 100000; i++) { label1.CreateGraphics().MeasureString("Test", label1.Font); } // Version 2 - Dispose called Label label1 = new Label(); for(int i = 0; i < 100000; i++) { Graphics g = label1.CreateGraphics(); g.MeasureString("Test", label1.Font); g.Dispose(); }
The only functional difference between the two is that the first version does not dispose of the Graphics object obtained with CreateGraphics. By the way, I also had a version that used a separate variable for the Graphics object. It looked exactly like version 2 with the line g.Dispose() commented out. There was absolutely no difference between the results for it and Version 1.
Even before breaking out the CLR Profiler, I tested them for speed. The version that called Dispose excplicitly ran 12-14% faster than the non-dispose version. The reason is apparent once you run the profiler on the application. The version that calls dispose has every Graphics object reclaimed each time the garbage collector is run. In the version that doesn't call dispose, the Graphics objects survive until the application terminates!
On my machine there were four collections in all for the non-dispose version. Two generation 0, one generation 1, and one generation 2. Every single Graphics object survived them all. The only reason I can think of that an object with no references in code would survive even a generation 0 garbage collection is that is being kept alive another way, such as holding onto unmanaged resources.
Here's the reason for the difference in performance. Each time an object survives a garbage collection, it may be moved closer to the bottom of the managed heap. For the version that calls dispose, the garbage collector needed to move 3 instances of System.Drawing.Graphics, which account for 48 bytes and .02% of the GC's attention. For the non-dispose version, the GC moved 30,689 instances, which is 491,024 bytes and 76.75% of the total. That's a huge difference.
The moral of the story is to call Dispose on objects that implement IDisposable, especially ones that are known to hold onto unmanaged resources, such as GDI handles or database connections or file handles. | | MigrationUser 1 Wednesday, March 02, 2005 8:38 PM | Very interesting... I have to admit that I don't know much about the garbage collection process; but I just learned a good bit! Thanks :)
So the reason to call Dispose() is specifically for the sake of the garbage collector.
I guess the question then becomes, which is more efficient; coding to aid garbage collection or coding to aid execution speed (that is, reducing the number of lines of code).
I took your two examples and made one change; I USED the value returned by MeasureString():
Console.WriteLine(label1.CreateGraphics().MeasureString("Test", label1.Font).Width & " -" & i)
Without doing a Console.WriteLine, the Dispose test takes about 1.85 seconds, while the No-Dispose test takes about 2.85 seconds. This is very consistant and quite a difference. But when you add the WriteLine statement, the times are within 1/100th of a second (~ 21 seconds each) with about an even split on which is the faster (sometimes Dispose, sometimes NoDispose).
When you also consider the fact that all of the tests in this thread are doing a minimum of 100,000 calls it seems unlikely that the overhead of the GC would negitivly impact the performace of the application given a reasonable number of calls.
I would have to say that if you are going to do thousands upon thousands of calls to IDisposable objects then you should probably Dispose() them for the GC's sake. But if you're only going to make those calls here and there, don't be affraid to code them inline. | | MigrationUser 1 Thursday, March 03, 2005 11:46 AM | I'll have to disagree. Many classes implement IDisposable because they hold onto unmanaged resources and the Dispose method is a standard way of telling the object to release those resources. Your example of choosing between aiding the garbage collector or aiding execution speed doesn't really hold up since aiding the garbage collector often increases execution speed. Speed is often not directly related to the number of lines of code.
Obviously, the large amount of calls to CreateGraphics in the examples was to illustrate the problem, but leaving one GDI handle or file handle dangling because dispose wasn't called on the object that held a reference to it is not good practice.
Unless you have specific knowledge that a class does not use the Dispose method to release unmanaged resources, you should always call dispose. The penalties for not doing so can range from performance degradation (from causing the garbage collector do more work) to IO exceptions because maybe you opened a file without disposing of the FileStream object that opened it.
My personal rule of thumb on this is simple: If a class implements IDisposable, trust that the developer of that class had a valid reason for it. | | MigrationUser 1 Thursday, March 03, 2005 12:17 PM | I guess we can agree to disagree on this one. =)
I didn't have a lot of time to plink with it today, but I did try a few other tests and could not cause an IO exception using inline reads of files, nor could I see a performance loss.
| | MigrationUser 1 Thursday, March 03, 2005 5:56 PM | FileStream stream = new FileStream("text.txt", FileMode.Open, FileAccess.ReadWrite); Console.WriteLine(new StreamReader(stream).ReadToEnd()); The first time it is run, the contents of the file are printed to the console as expected. If run again, a System.IO.IOException is thrown with the message: The process cannot access the file "{filename}" because it is being used by another process.
If you call Dispose on the FileStream or the StreamReader, the handle to the file is released and all is good. No exceptions.
I haven't seen one piece of documentation that says it's ok to not call Dispose on IDisposable types, yet urgings to call Dispose on IDisposable types after you're done using them are found throughout the BCL docs. That's the whole reason the disposable pattern exists. It was even important enough for the designers of C# to include a keyword for it (using). From what I understand, the next version of VB.NET will also include a using-like construct to make disposing objects easier.
I can understand letting it go in a quick-n-dirty throwaway app, but failing to free recources in a commercial-grade application is just not good programming. In many cases, the whole system is affected by it. In the above example, try opening the file in Notepad while your app still has a read/write handle. You will not be able to save any changes.
Honestly, I really don't see this as a matter of opinion, but I guess that's just my opinion :) | | MigrationUser 1 Thursday, March 03, 2005 6:53 PM | Isn't that just an issue of not closing an open file? Does it have anything to do with Disposing an object? I can't find a Dispose() method on either the FileStream or StreamReader objects...
I guess I've just been lucky in my coding... I never would have executed the code in that way... I would have used:
Console.WriteLine(New System.IO.StreamReader("text.txt").ReadToEnd)
... which you can call over and over without getting an exception; the file is implicitly closed after the inline call.
I definately agree that Dispose() should be called if you create an instance of an object that implements it; I'm just not convinced that it is necessary to create that instance for no other reason than to execute Dispose() against it. | | MigrationUser 1 Monday, March 07, 2005 12:54 PM | Yes, it's a case of not closing the file. Calling Dispose closes it. The whole point of Dispose is to release those kinds of resources. File-based classes generally have a Close method which calls Dispose internally. The designers of the BCL thought that Close was a more natural name or file operations, but that's purely semantics.
You can't find a Dispose method on StreamReader because it is implemeted explicitly. As in:public void IDisposable.Dispose(){}Because of that, you would need to have a reference to an IDisposable object to call Dispose.//C# ((IDisposable)reader).Dispose(); 'VB.NET CType(reader, IDisposable).Dispose()That is the negative side of explicit interface implementation. Many people won't even know the methods are there.
By the way, I wouldn't code it the way I showed, either. I was just showing you that an exception could be caused by not Disposing of an object because you said you couldn't get it to happen.
By the way, a file handle is still left open with your one-liner example. The handle is <i>not</i> closed after each call. No exceptions are thrown because it's a non-exlusive, read-only handle. But it is still sitting there waiting to be returned to the pool of available resources.
Either you misworded your last statement or you have a pretty substantial misunderstanding of what is happening when you write a one-liner like you showed. You are still creating an instance of an object, you just don't have a reference to it.
That's the kind of thing that leads to memory leaks and resource leaks in non garbage-collected environments. You create an object, but don't have a reference to it so you can release the resources. The same thing is happening here, it's just that the garbae collector cleans up after you. But it doesn't clean up right away. Resources are sitting in your machine's memory and wasting space and processor power because you didn't explicitly dispose of them.
Is short, not calling Dispose on IDisposable type can cause exceptions (in some cases), temporarily leak memory and resources, and make your application (and possibly the entire machine) slower.
For my money, a few lines of code saved isn't worth it. I'm getting paid to do this, so I may as well do it right. | | MigrationUser 1 Monday, March 07, 2005 7:55 PM | Thanks for the inside info... I was certainly one who never would have known the method was there. How can you find out what other objects use this explicit implementation? Look at the help doc/object browser for every object and see if it traces back to IDisposable? There must be a better way...
Using Task Manager to display the Handle count of the app (as before with GDI objects) shows what you said about the file not being closed immediately after each call. It's only closed when the system determines that more resources are needed, and those unreferenced handles are the priority objects to reclaim. So in the case of reading a file, yes another application, such as notepad, is not allowed to update the file in question until the GC reclaims that unreferenced handle. But in the case of the previous CreateGraphics example, the GDI object is reclaimed immediately... Is that because a GDI handle has a higher priority to the GC than a file handle? I guess this is where they say that you can't count on the GC to reclaim a given resource at any specific interval...
I meant what I said in the last statement of my previous post; I realize an instance of the object is being created without a reference - what I was saying is that "I" didn't create that instance; the CLR did.
I think you hit the heart of the problem I'm having here when you said "You are still creating an instance of an object, you just don't have a reference to it... That's the kind of thing that leads to memory leaks and resource leaks in non garbage-collected environments."; since this is a garbage-collected environment, can you really cause a resource leak that continues for the life of the application or beyond? The worst problem I've been able to cause so far with these inline code examples is a temporay file lock; resource management seems to take care of itself as necessary.
I also got some info on this from the thread: http://www.windowsforms.net/Forums/ShowPost.aspx?tabIndex=1&tabId=41&PostID=23398
Combining what was said there with what's been shown here, I have to currently conclude that:
Creating a reference to an object so that it can be explicitly disposed is important when: A: You need the highest possible performance from the GC B: You're working with objects that are locked while a handle to them exists
Other than the two instances above, in a real application I don't think you're going to see any difference between the GC cleaning up after you or doing it all yourself... That seemed to be the general conclusion of the other thread as well; if you haven't broken into unmanaged code, you can pretty well rely on the GC to do the cleanup.
I really appreciate you helping me through this. I'm not a paid programmer per-say; I program apps for my company as an aside to running a network. That doesn't mean I want those apps to be junk, but they don't have to be absolutely as efficient as possible. Processors are fast and memory is cheap; my time is slow and expensive! I need to make things that work and work now, while still keeping the fatal flaws out (all the while working with only a BASIC background, a MSDN subscription, and this website - so please bare with me).
Getting to pick the brains of people with educations and experience in this stuff is really helpful. I truly thank you for your posts and hope I haven't been too much of a butt in arguing with you. =) And if my conclusion above is still way off, please slap me around some more and try to set me straight!
Thanks cwilliams,
-rkimble | | MigrationUser 1 Tuesday, March 08, 2005 2:02 PM | Nothing wrong with a spirited discussion :)
I think your assesment of when it's important to call Dispose is pretty accurate. The performace aspect is not always directly realted to the garbage collecter, per se. It can also be things like database connections left open and such.
But you're exactly right that you have to balance the benifit that you'll get from the app with the time it takes to develop it. A quick utility app needs to work first and be an exceptional performer second. It's not uncommon for me to entirely skip trying to optimize an app if it's not doing anything of major importance or won't get used much.
I'm also a network administrator. In fact, you could probably teach me a few things :)
I spent this this past weekend replacing our server (we're just one server with about a dozen users) with a new Small Business Server 2003 machine. I've learned a lot over the past few days as I've tried to iron the kinks out. Microsoft really turned the screws on the security aspect. That's a good thing, of course, but it can make it kind of difficult to get everything working. I never realized how many applications need to write back to their application directory or the system directory or HKLM. I'm lucky I still have some hair left :)
My only challenge left unsolved so far is getting ActiveSync to work on a client machine without elevating the user's priveledges too much. | | MigrationUser 1 Tuesday, March 08, 2005 2:48 PM | I avoided the Small Business edition of Server 2003 for just that reason =) with standard edition you pretty much get to turn the screws yourself (other than those related to the internet; they're turned pretty tight in any edition), since you're deciding what services run where. But we've got four 2003 servers plus two 2003 appliances (storage and vpn) so I've got some options as to what services come from what machines.
Are you using Group Policy to control your workstations and/or users? (assuming your running a domain and not a workgroup) I'm pretty sure there's a sample policy for ActiveSync burried somewhere in the MSKB... though you'll have to be careful about conflicting with any other existing policies - but the new resultant set of policies wizard will show what actually gets applied to a given user (its like the "effective permissions" view of the security on a file).
I typically leave my users with about the same permissions as the "Power User" built into the workstation. Of course, my users are lucky to remember how to use Outlook, let alone reconfigure a system setting incorrectly; so I let them roam their own boxes pretty freely. It's just when they leave their box that they get locked down.
If you've got any questions, I'd be happy to take a shot at answering them. We're doing the whole kit-n-kaboodle here; AD, Exchange, SQL, MSRAS, iSeries integration, etc. - so when it comes to "making windows do it", chances are I've tried. =)
I don't have a preferred networking forum so I'm not sure where this discussion would be appropriate, but we should probably not have it here. I try to stay on topic in a forum - in fact, I wish our great Dispose() discussion would get moved to it's own thread since it doesn't really belong in this one beyond our first posts! | | MigrationUser 1 Tuesday, March 08, 2005 6:45 PM | I agree about staying on topic.
That's actually the gripe I have with the now quite long "event on scanning barcode" thread. I didn't want to post in the thread asking the gentleman to post new questions on a new thread because it's not my place, but it's getting a little rediculous. The title of the thread is so specific that people browsing the forum would never think to look in it to find all your great answers about SMTP, reporting, connection strings, string manipulation, etc. Not to mention I'm sure you feel just a tad obligated to answer the guy since he's directing all the questions specifically at you.
At least this long Dispose thread arose naturally from the original question :)
Ok, with all that talk of staying on topic, I'm really trying to bite my tongue so I won't start asking you about all my networking issues :)
Suffice it to say, I solved (for now) the ActiveSync problem by giving the affected user administratvie rights on his machine, but I'm not happy about that at all. I Googled quite a bit, but I couldn't find any references to similar problems. I did find a couple things that said you must be an administrator to run ActiveSync because it needs to write to HKLM, but it just seems silly that such a widely used application would have that requirement. It seems stupid to harden my whole network and then make someone a machine adminstrator so he can take his Outlook tasks with him when he's in the field. I'm still considering this to be a temporary solution.
As far as Group Policy, a few came preassigned, including one to poke a hole in Windows Firewall for ActiveSync. I verified it took affect on the client machine, but still no go.
I know the gist of Group Policy, but I'm certainly not an expert. I just took delivery of a <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0782142982/qid=1110334984/sr=8-1/ref=sr_8_xs_ap_i1_xgl14/104-6642534-8027948?v=glance&s=books&n=507846">Group Poilcy book</a> from Amazon yesterday, so I've got some reading to do.
Man, between .NET books, Windows administration books, and Photoshop books, I'll have enough to read for quite a while.
Darn it, I'm off topic again! | | MigrationUser 1 Tuesday, March 08, 2005 9:33 PM | You're absolutely right about that other thread. I should have stopped it long ago. I just left a post on the subject, so the thread should be able to end now. :)
The only aditional thought I had on the MSASYNC thing was that you might only need admin privs for establishing the inital partnership, and then switch back to a standard user. So long as the user always docks on the same USB port there shouldn't be a device configuration change that would require extra privs. Now, with the bit on the regkey, you might find the specific key in HKLM and give the user permission to it. I haven't tested that first bit, but it sounds feasible. And it's a pretty simple matter to give a specific user specific access to a specific key. :)
Anyway, if you want, feel free to email me at the username and domain listed on my posts.
Yup, enough topic jump'n for this thread too. =) | | MigrationUser 1 Wednesday, March 09, 2005 3:16 PM |
|