Cluebat-man to the rescue

A weblog dedicated to Visual C++, interoperability and other stuff.

Posted: by

Comments

Cluebat-man to the rescue said:

Last week I was debugging a Windows hook function, and I discovered a side effect of installing a hook:

# October 9, 2006 9:04 AM

Cluebat-man to the rescue said:

This article is a follow-up on a previous article: 'Manipulating LabVIEW arrays in C++' in which

# October 25, 2006 8:04 AM

ronpih's weblog said:

# November 12, 2006 5:06 PM

ronpih's weblog said:

Nice series of posts from TechEd Barcelona (including reviews of some VC++ sessions): http://msmvps.com/blogs/vandooren/archive/2006/11/07/Tech_2D00_Ed-developers-Barcelona_3A00_-Monday.aspx

# November 12, 2006 5:31 PM

ronpih's weblog said:

Nice series of posts from TechEd Barcelona (including reviews of some VC++ sessions): http://msmvps.com/blogs/vandooren/archive/2006/11/07/Tech_2D00_Ed-developers-Barcelona_3A00_-Monday.aspx

# November 12, 2006 5:31 PM

Damjan said:

Hello!

Your solution is usefull also to solve an old problem here in my lab.  I'm new in C#. I have a problem with your sourcecode.  My compiler (Borland Dev.Studio) can't load the:

using System.Collections.Generic;

Any ideas?

 regards, Damjan

KRIZMANCIC at TASC dot INFM dot it

# December 12, 2006 2:46 AM

Tom Widmer said:

VC2005 inserts appropriate memory barriers for volatile accesses when you use a CPU that requires them. The code should be safe for all CPUs with VC2005. This is also true for Intel C++ (although volatile's behaviour is controllable by a compiler switch there, so care is required).

Another possible enhancement would be to drop the use of volatile, and instead insert the appropriate memory barrier operations directly (the old "volatile is neither necessary nor sufficient in general" adage). This is more explicit, and can be more efficient on some architectures, since you are explicitly specifying the exact barriers you require, which gives the optimizer more freedom.

Another enhancement you could add would be blocking read and write methods, though that's trickier, since you need a fast-pathed event object to avoid kernel calls where possible.

# January 5, 2007 3:55 AM

Joe Ziegler said:

I am getting the error...

VI version is later than the current LabVIEW version.  An error occurred loading VI 'CleanupCallback.vi'.  LabVIEW load error code 9: VI version (8.2) is newer than the LabVIEW version (8.2b23).

Strange....its basically the same version.  Just off by a little b23.  You would think a newer version would be somewhat backwards compatable. Guess not.  So, how can I find out what is in "get callback function pointer" and cleanup callback?  Great article..can't wait to impliement it.

# January 18, 2007 11:31 PM

Joe Ziegler said:

Updated Labview and it worked flawless.  This is awesome.

Thanks, Joe

# January 21, 2007 1:53 AM

George Puiklis said:

Hello Joe,

How did you update labview? I mean 8.2 is the latest version. I am getting the same error you did (for another vi though).

George

(mail: gpouikli at ee.duth.gr)

# February 1, 2007 9:33 AM

ANNONYMOUS said:

YOU NEED TO DESIGN MFC FOR USB

# February 7, 2007 1:22 AM

silver said:

Hi Joe

I have the same problem that you had.

Can you tell me how can I update LabVIEW

# February 11, 2007 11:20 AM

Rod Trent said:

Sorry, dude.  Firewire never really caught on in the industry, which is why the option is not in Vista.  Are you still using floppy disks?  :)

# March 7, 2007 8:59 PM

vanDooren said:

As it turns out, yes I am.

Not that I wanted to, but guess what you need if you want to install XP on a hardware RAID1 mirror set?

Btw, I would not compare FireWire to the Floppy.

It is the fastest bus available for computer to computer data transfer, and it is available on all modern medium to high end motherboards.

# March 8, 2007 2:58 AM

Rexxar said:

Hi

Thanks very much for this link, but I have a question when the callback function is a VI code and the caller is a dll function. Could you please read this post and give me a reply to"paulbin@126.com"?

Thanks very much again.

# March 23, 2007 12:28 PM

Rexxar said:

# March 23, 2007 12:29 PM

... said:

luogo fine, sapete..

# March 31, 2007 8:19 AM

/\/\o\/\/ said:

Glad to hear you liked the PowerShell session,

also nice to see you noticed the divider example :

>Everybody seems to find it perfectly obvious that 44/7 equates to 6.2857

As PowerShell is a Shell, the engine has a powerfull type converter and does a lot of parsing to make interactive use easier,and to behave as an admin would expect.

to force PowerShell into a more developer like behavour cast it explicitly :

PoSH> [int](44/7)

6

I'm sure you will enjoy the book.

Greetings /\/\o\/\/

# April 1, 2007 10:08 AM

roy said:

i don't get it. " Each of the pointers (indices really) is updated only by one thread.". Why? can you please explain a bit more?

# April 13, 2007 12:35 PM

vanDooren said:

Hi Roy,

The idea of a producer consumer queue is that there is one producer shoving items in on one end, and 1 consumer pulling out items on the other end.

Formally this is known a a first-in first-out queue mechanism (FIFO).

Only the producer updates the write pointer, and only the consumer updates the read pointer.

Because of this there is no reason to synchronize access to the pointers.

Since the update is done AFTER the data access, there is no risk of overwriting items before they are read, or reading an item before it's completely filled in.

# April 16, 2007 6:41 AM

... said:

Scommettevo che avete speso molto tempo lavorare al luogo. Ha valso la pena di fare;)

# April 21, 2007 4:14 AM

... said:

Scommettevo che avete speso molto tempo lavorare al luogo. Ha valso la pena di fare;)

# April 21, 2007 4:14 AM

... said:

Interesting comments.. :D

# April 21, 2007 8:07 AM

... said:

Interesting comments.. :D

# April 21, 2007 8:07 AM

... said:

Lavoro eccellente! ..ringraziamenti per le informazioni..realmente lo apprezzo: D

# April 21, 2007 11:47 PM

G said:

I think this is not guaranteed to work if there is more than one thread on either end, if I understand correctly?  

# May 23, 2007 12:35 AM

Cholo Lennon said:

The DevDays in Argentina are the same... VC++ simply doesn't exist, VB and C# only...mmm... this doesn't smell well...

Regards

# June 19, 2007 8:33 AM

Kumar said:

I just want to know is it possible to

Create a thread safe producer consumer CircularQueue in C++ without using locks ...

# June 20, 2007 2:54 AM

Cluebat-man to the rescue said:

As I already mentioned before, it's a small world after all. (Hearing that annoying Disney tune yet

# July 1, 2007 4:02 PM

Satya Shyam K Jayanty said:

Congratulations on new arrival in your family, happiness and health to Nele ;-)

# July 23, 2007 10:08 AM

Ken Cox said:

Very interesting reading.

Thanks for posting this for those of us who can't be there.

Ken

MVP [ASP.NET]

# November 5, 2007 8:54 PM

ytosa said:

What is the corresponding C# "out" for C++/CLI?

C# you can have foo (out int [] a)  or foo (ref int [] a)

I know how to do the second one in C++/CLI like

foo( array<int>^% a)

Can you tell the first one in C++/CLI?

# November 10, 2007 11:07 PM

Arris said:

Great article that clarify the intricacies of copy contructor and assignement operator

# November 22, 2007 3:14 AM

Arris said:

Great article that clarifies the intricacies of copy contructor and assignement operator

# November 22, 2007 3:14 AM

Biff said:

> Kumar said:

>

> I just want to know is it possible to

> Create a thread safe producer consumer

> CircularQueue in C++ without using locks ...

Yes, but the code above won't generally work unless.

1. The m_Read and m_Write updates are atomic

2. PushElement: Has a memory barrier around the data copy and m_Write update

3. PopElement: Has a memory barrier around the m_Write access in the if() clause and the data read.

# November 26, 2007 10:24 AM

vanDooren said:

OK this is royally late, but to G: this will work in a multithreaded situation without any problems. EDIT: Apparently I misunderstood G. If there is more than 1 reader or 1 writer, then this code will not work as intended.

# November 26, 2007 11:40 AM

vanDooren said:

Thanks. It's nice to know that someone likes it.

# November 26, 2007 3:03 PM

vanDooren said:

Hi sorry for the late repIy. It was a busy couple of weeks.

I have not really looked into this, but C++ does not know the notion of output only.

It is either by reference or by value.

DCOM has the notion of input / output or both, but that is a marshaling issue. Marshaling only takes place for the IO direction that was specified in the IDL declaration.

# November 26, 2007 3:09 PM

eugene kim said:

I'm not sure about what Biff said, (#2,3)

Why do we need a memory barrier around data copy and m_write for instance?

There won't be any reordering for those in a single thread.

Thus we don't need to worry about those here(in 2 threads) either.

# December 3, 2007 9:38 PM

Cluebat-man to the rescue said:

One of the issues involved in running a pharmaceutical process network is that QA has to give its blessing

# December 4, 2007 6:12 AM

Allen said:

I got a question about the multithreaded case.

>OK this is royally late, but to G: this will work in a >multithreaded situation without any problems.

     bool PopElement(T &Element)

     {

           if(m_Read == m_Write)

                 return false;

**** what if some other thread changed the value of m_Read at this point?

           int nextElement = (m_Read + 1) % Size;

           Element = m_Data[m_Read];

           m_Read = nextElement;

           return true;

     }

# December 16, 2007 9:29 PM

Domaining - Information on Domains and Domaining » Active Directory disjointed namespace problems said:

Pingback from  Domaining - Information on Domains and Domaining &raquo; Active Directory disjointed namespace problems

# January 22, 2008 1:31 AM

noobo said:

Have fun crashing just by looking at those two lines you should obviously see that this cannot be threadsafe:

           int nextElement = (m_Write + 1) % Size;

EDIT: Yes this is threadsafe. Size does not change over the lifetime of the object, and m_Write is only written to by the writer

           if(nextElement != m_Read)

Unless your CPU has an atomic +1 % Size compare instruction

It will do something like

1. Reg = 1

2. Reg = Reg + m_Write

3. Reg = Reg % Size

4. Compare Reg, m_Read

If another thread now updates m_Write during step 3 and 4 you have a serious Problem.

EDIT: Since the queue has only 1 writer, there is no problem. With multiple writers your point is valid, but this queue is meant for 1 writer hread and 1 reader thread.

Same goes for the position counter updates when you want multiple writers or readers you'd need to use Interlocked functions here or a Mutex.

EDIT: True, but again this class is only meant for single reader and single writer scenarios

The minimal needs to get a single reader/ single writer queue  threadsafe are 2 Semaphores to handle the full/empty checks.

EDIT: No, it is not. 32 bit reads and writes are atomic. The writer checks before each update to see if there is at least one free element. It does not matter if the reader reads more or not, because that will only make more items available.

Similarly, If the reader checks if there is at least 1 element in the queue, the write pointer may be updated or not it doesn't matter. Only 1 element will be read or written at a time, so if the write pointer changes, that doesn't matter.

For adding multiple multiple readers and writers you've to make the m_Read/m_Write increments atomic. For example use InterlockedIncrement or place a Mutex around the lines.

This problem has extensivly studied btw:

en.wikipedia.org/.../Producers-consumers_problem

EDIT: I just read the article, and the only reason semaphores are needed is because the reader and writer share a wakeup mechanism. The buffering itself does not need semaphores or protection in the single reader single writer scenario.

My class does not have or need a wakeup mechanism, so they don't need semaphores either.

For safe wakeup you need a semaphore indeed (which is why my production code has them for exactly this feature) but the queueing mechanism itself needs no protection. Which you could have figured out for yourself, had you read the article more closely

To proof invadility of your code you could do this btw:

...

int nextElement = (m_Read + 1) % Size;

Sleep(10 + rand() % 10);

Element = m_Data[m_Read];

...

EDIT: Yes I know, but ONLY if there are multiple readers! The m_Read is only updated in PopElement, and only 1 thread is ever calling that Method, so no matter how long you sleep in there, it will never go wrong.

Now make 1 writer thread that writes in 1 2 3 4 5 6 etc

And make 10 reader threads you'll for sure get duplicate numbers and some missing numbers in the output.

Yes indeed. That's why it's described as a class for a single reader and single writer. Which I also explain under the heading 'Why this is safe'

 

# January 25, 2008 6:41 PM

Jens-Birger schlie said:

Thanks a lot!

Had this problem too, though you was the first to find a solution.

:)

Many Regards,

Jens- Birger Schlie

# February 1, 2008 2:04 PM

s said:

You need to change your reply to G. G was already asking what if there are multiple readers or multiple writers (and you obviously misunderstood his question). That's why both Allen and noobo critiqued your code (and you never clarified in your original article that it's a single-producer single-consumer que).

# February 28, 2008 5:57 PM

vanDooren said:

I just read back, and I didn't explicitly say that it was single reader / single writer.

I mention 'the reader' and 'the writer', and that it is safe because only 1 thread will do either reading or writing so it was implied.

But not obvious enough unfortunately.

# February 29, 2008 5:29 AM

Minherz said:

You can use IMEX=1 extended property when defining connection string for OLE DB driver and get all cells for all columns as text.

# March 4, 2008 2:59 AM

vanDooren said:

I tried that, but it did not work completely.

The fields that had only numbers were still parsed as doubles, while the fields that contained a mix were passed as text. I needed all fields to be passed as text, and I don't think IMEX does that.

IMEX only prevents those mixed fields from being passed as DBNull. At least in the test I did.

Which version of Excel did you try this with? I used Office 2003 SP2

# March 6, 2008 1:18 AM

J. Passing said:

That's true. But there was a time when VMWare actually worked on FreeBSD and I used to rely on it quite heaviliy (I think it was up to VMWare 2.0). I do not remember if it just worked or if it was indeed a platform officially supported by VMWare. But given this fact, I consider it even more unfortunate that it does not run on BSD...

# March 17, 2008 5:48 AM

vanDooren said:

I read something to that effect.

Someone maintained a linux compat port that supported running it under linux compatibility.

Too bad linux seems to get all the hype.

I've used both linux and BSD, and I liked BSD a lot more than linux.

Then again for me it doesn't really matter, since we are a native windows environment.

# March 17, 2008 4:50 PM

michael said:

I used to have that dilbert strip, but can't find it anymore. do you have the date from it?

# April 30, 2008 12:06 PM

tomsun said:

I'm new in .net, I have the similar problem, I need your help.

The following is the prototype of the callback function:

VOID(_stdcall *VideoFilter)(VOID *pcontext, BYTE *pData,ULONG dataLength)

Can you give me the corresponding .NET code?

Thank you in advance.

# June 14, 2008 7:43 AM

Anuj said:

would u please tell me the diifference b/w c++ and c++/cli?

# July 1, 2008 1:03 AM

Ivan said:

I am absolutely agreee with you. I work in company wich uses this syntax and we`re tring to migrate C# but our CEO does not want to do this.  

# July 11, 2008 7:30 AM

vanDooren said:

Well, it doesn't have to be C#. C++/CLI is a good option if you have lots of C++ code.

But MC++ is evil incarnate.

# July 11, 2008 2:40 PM

JP said:

That's why there is the VBA option "option compare database" that Access uses by default for all VBA code. I think case insensitiveness is also the default behavior for SQL server and most other DBMS -- so I think Access is not really to blame here... But that does not change the fact that Access can be a pain to work with ;)

# July 14, 2008 4:29 AM

vanderghast said:

Most MS SQL Server installation are also 'case insensitive', like Jet. So, based from your comment, changing Jet to MS SQL Server everywere, yield the same observable effects, so,... MS SQL Server ain't a database?

Na, sounds more you are just too fast to blame other of your (wrong) assumptions about code.

# July 16, 2008 12:31 PM

vanDooren said:

It is true that my asumption on the default with SQL server was wrong. I checked, and the default is insensitive.

With SQL Server you can choose whether you want case sensitive or not. And if you have a database in which strings are keys, then case sensitivity is the only way to insure data integrity when the clients are case sensitive.

With Access you can't, so you are forced to implement case insensitivity in the client, in each place where you use those keys for joinings or lookup.

Normally I'd use identity ints, but this is a database I got from someone else, so string it was.

# July 17, 2008 1:34 AM

a said:

If the queue size is one, the writer will not be able to use it.

# August 12, 2008 7:54 PM

vanDooren said:

That is correct. The queue needs to contain more than 1 element. Otherwise the system of index calculations does not work.

Every queue that I implemented using this class was expected to contain a known maximum amount of items.

This number was calculated by looking at its purpose, and then putting a sensible number on it, multiplied by 10 or 100 to be safe.

Most queues only contained short items (32 -128 bytes) so the overhead was minimal.

Even if that was insufficient, that error condition was handled and properly diagnosed so that it could be changed.

There are 3 reasons we didn't use dynamic allocation:

- performance. This was the memory only needs coyping, not allocating which could fragment memory, and would also cost time

- reliability. Once the queues exist, no error conditions can occurr anymore (no failed allocations).

- constraints. Most of those queues were layed out in a special block of memory on a PCI card, which was mapped into the address space. That memory was replicated via an optical interface so that all computers saw the same data. Since the location of anything had to be known by everyone, dynamic allocation would make things much more complex, error prone and less performant.

# August 13, 2008 12:54 AM

Martin Boecskoer said:

Hi,

i am trying to implement a hardware in labview for my master thesis (sports science). as i am no programmer i am having not enough knowledge to get the things working right.

the problem i have got is simmilar to the one you solved.

i need to suply a function with a pointer to a callback function defined as

typedef BOOL (_stdcall *CHANNEL_EVENT_FUNC)(UCHAR ucANTChannel, UCHAR ucEvent);

it is used in

_declspec(dllexport) void ANT_AssignChannelEventFunction(UCHAR ucANTChannel, // Initialize Channel pointers

                                   CHANNEL_EVENT_FUNC pfChannelEvent,

                                   UCHAR *pucRxBuffer);

used in the c file as following

__declspec (dllexport) void ANT_AssignChannelEventFunction(UCHAR ucLink, CHANNEL_EVENT_FUNC pfLinkEvent, UCHAR *pucRxBuffer)

{

  sLink[ucLink].pfLinkEvent = pfLinkEvent;

  sLink[ucLink].pucRxBuffer = pucRxBuffer;

}

if you could help me by solving the problem i would be very thankfull or if you could make things work. i would pay for it too.

thank you a lot

martin

(email martin.boecskoer(at)aon.at)

# August 21, 2008 4:04 AM

Pops said:

Start slow and taper off...

# August 26, 2008 4:17 PM

Cluebat-man to the rescue said:

I found this list of article on Raymond's blog . Raymond's blog is one of the more interesting

# September 12, 2008 5:51 AM

Alun Jones said:

References are shorthand for pointers.

There's no other way to get it in a programmer's head what references really are. Sure, technically, they are slightly different, and it's possible that a new compiler technology may one day come along in which references and pointers are separated - but it's still going to behave in the ways that you've outlined.

Of course, as an ex-Fortran programmer (everything is passed by reference, even constants!), it's not too hard for me to deal with.

# October 2, 2008 2:17 PM

ismo said:

It seems that next IEEE standard might have something called "64-bit decimal floating-point" which will address the original problems with floating points and decimal numbers.

This paper has more info on subject

ieeexplore.ieee.org/.../login.jsp

# October 9, 2008 7:28 AM

Rod Trent said:

We need to rid ourselves of mechanical hard drives before we'll see instant-on computers.  However, this is something that does need to happen, in my opinion.  If you've had any experience with the end-user side of computers, you'll realize its not the logging in part that is the delay.  Sure it adds a little extra bit of boot time, but the speed of a computer degrades over time, no matter how effective you are at managing performance through diagnostics and tune-ups.

I'd gladly welcome Instant-on.

# October 17, 2008 6:53 AM

AR said:

TY.PC in my room has no net, hooked up a external HD to my friends,tried installing,but that didnt even work.Thanks for this info.

Fing BS  they dont include a install disc.BS that you need a net connection for a mp3 player.

# November 15, 2008 4:58 PM

vanDooren said:

You're welcome.

It is indeed very frustrating that Microsoft wants to force the whole 'live' thing down our throats. That's what you get when marketing runs the show, I guess.

I understand that this has a lot of benefits, but there are so many cases where the automatic install is not possible, that they should have foreseen it.

# November 16, 2008 6:34 AM

Alien said:

Very useful code, thanks. I was had exactly the same requirement when I came across it.

One note: the actual FIFO depth (the effective buffering achievable) is Size - 1, not Size.

# November 25, 2008 6:22 PM

Corey said:

Thanks, however, my computer my computer says that my computer does not enough storage at step 5. However, I doubt that it needs that much GB. Got any ideas?

# November 28, 2008 1:27 AM

JC said:

This implementation does not work for one reason, and is not useful for much more than a thought experiment for another reason. First, it doesn't work because of the following race condition in PushElement:

1            int nextElement = (m_Write + 1) % Size;

2            if(nextElement != m_Read)

3           {

4                  m_Data[m_Write] = Element;

5                  m_Write = nextElement;

6                  return true;

7            }

If any producer thread gets to line 2 while any other producer thread is executing 2-5, both will have the same m_Write index, and an item will be lost in the queue.

This is a fairly obvious issue, and also highly likely given how much time other threads have to race in and screw things up. This is a major problem.

However, that aside, the second major problem is more related to the intended functionality. This queue provides no way for a reader thread to idle on an empty queue. Therefore, readers are doomed to either poll the queue in tight loops when it is empty (not ideal if your CPUs are busy doing other things) or they must insert arbitrarily long pauses when the queue is empty (not ideal if latency matters). This problem makes this implementation not useful in most real scenarios, in addition to not being thread-safe.

JC

# December 1, 2008 7:51 PM

JC said:

PS, a similar and equally obvious race condition exists in pop:

1            int nextElement = (m_Read + 1) % Size;

2            Element = m_Data[m_Read];

3            m_Read = nextElement;

It should be easily identifiable. If any consumer thread gets to 2 while any other consumer thread is executing 1-3 then both consumer threads use the same value of m_Read, thus returning the same item twice.

This implementation is far from thread-safe, and not for any subtle reasons having to do with memory barriers, optimizations, etc. It's just plain broken. Locking around the lines I mentioned to make the operations atomic would provide the necessary safety.

A second option on Windows is to modify the read/write indices using InterlockedIncrement(), which does atomic increment and returns of an integer (search for InterlockedIncrement on msdn.microsoft.com, there are many other Interlocked* functions as well), instead of attempting to do it over a few non-atomic statements and crossing your fingers that another thread doesn't run at the same time.

JC

# December 1, 2008 7:57 PM

vanDooren said:

You are absolutely right, but

1) I already explained that this queue is supposed to work with 1 reader and 1 writer. And yes, I know that if that isn't true, it won't be safe.

2) With multiple readers / writers you can still make it threadsafe without locks, but then every reader needs its own read pointer, and each write pointer needs its own write pointer.

3) You can add a semaphore or event object to signal once for each element that gets written in the queue and solve the problem you mentioned

# December 2, 2008 12:58 AM

dobrokh said:

Thanks for simplicity. And do you known how can I do this in SmartDevice (Native) projects?

# December 9, 2008 1:22 PM

vanDooren said:

I asked the C++ team about this a year or 2 ago, and they had no plans at that time to support mixed mode compilation on smart devices.

I had to resort to making a native DLL with my native functionality, using only C style insterfaces (exported functions)

Then I made a C# Library project, using PInvoke to import the native functionality into the .NET classes.

# December 10, 2008 2:56 AM

dobrokh said:

I see one PPC program has been written in mixed mode. But it may be throught COM ?

# December 10, 2008 8:59 PM

vanDooren said:

Do you have a URL?

Mixed mode refers to using the /clr compiler switch to compile a mix of native C++ code and managed C++ code within the same module. It was not possible with VS2005. I haven't checked VS2008, but in April 2007 they had no plans to support it.

Using other techniques to use native code in managed projects is called interop.

COM interop is another way it could be done.

In that case, the native and managed part are still split in different modules, but there is a runtime marshaler in between, performing all communication.

It is not as simple as making a C style DLL as I described earlier, but it has the advantage that you can work with classes.

# December 11, 2008 1:17 AM

dobrokh said:

Now, I can't access to guy with that very interesting project.

I work in mobile device programming sector. But some reason for to leave in C++ (big-big-big old C/C++ code). Then -> Native C++ (Smart Device projects). But I known, that big part of OS (Win CE, especially GUI-elements) has been written in .NET.

IMHO: "native way" for using that elements - using Managed C++. But "Managed C++ in SmartDevice projects" - it is not easy...

How can I do it? (very bad question, IMHO, but - "no way for me":)

# December 11, 2008 10:58 AM

John Lim said:

Hi,

I am not sure if this question is in the same vein as what you have blogged on "Preventing a DLL from being unloaded by the app that uses it ". But essentially, i have implemented a hook similar to the code snipped you provided on "The windows hook hack" article. The problem is when I run(debug) my application that uses this dll, I am unable to recompile my dll after I have closed the application that was using this dll. The error message received was "error PRJ0008 : Could not delete file 'c:\...\mydll.dll'.

Somehow the dll is still being used and it cannot be deleted.

Can you please help? Thanks.

# December 16, 2008 8:50 PM

vanDooren said:

Hi, yes.

This is caused by the fact that the hook as I show it is a global hook (because thread id is 0). This means it is hooked into every thread in the same desktop.

This also means that your DLL stays loaded, even if your app exits.

To solve this, use UnhookWindowsHookEx to unhook the DLL before the calling application exits. that way the hook is released, and your DLL can be unloaded

# December 17, 2008 2:32 AM

Cluebat-man to the rescue said:

One of the things that everyone has to do sooner or later is parsing command line arguments into program

# December 17, 2008 5:44 AM

John Lim said:

Hi vanDooren,

Thanks. Currently i call UnhookWindowsHookEx in dllmain when DLL_PROCESS_DETACH is detected. I suppose thats not the right way?

Thanks,

John

# December 17, 2008 9:16 AM

vanDooren said:

As you already discovered, that won't work.

DLL_PROCESS_DETACH notifications are only sent when the DLL is unloaded. But the DLL won't be unloaded because there is a global hook active. :)

The existence of your hook prevents the unload which would remove the hook.

There are a couple of things you can do.

1) get the application that loaded the DLL to call a function in your DLL that would unregister the DLL.

2) use GetModuleHandleEx with the GET_MODULE_HANDLE_EX_FLAG_PIN, or my LockLibraryIntoProcessMem fucntion to lock your DLL into memory, instead of using the global hook hack. that way it will be unloaded when the calling process ends.

3) inject a thread in the process that create the hook (CreateRemoteThread api) and have that thread call a function in your DLL which unhooks the windows hook

4) store the hook handle in a global data section, export a fucntion from your DLL that unhooks that handle, and have a separate application call that function. The handle needs to be in a global section because otherwise, the second application will look at a different variable (normally, each process has its own copy of the DLL globals). You also need to make sure that no 2 hooks are installed (so you have to use  something to signal existence of the hook) and it could still fail if the hook handle is thread local.

I've put them in order of ease. I am relatively sure that 3 should work with minimal effort. If you have to go for 4, remember that I didn't try any of this, so the end result would probably need tweaking to make it work in your usage scenarios.

Kind regards,

   Bruno.

# December 17, 2008 10:42 AM

John Lim said:

Hi Bruno,

Thank you so much. It definitely helped. :)

My original intention was to have my dll create a hook so that I could retrieve WM_GETMESSAGE (DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE to be precise) so that the dll would know when my generic usb HID device was attached/removed. Suffice to say my dll never got that far. I know this is probably off topic but would you kindly point me to the right way of doing that as well?

Thanks,

John :)

# December 18, 2008 10:52 AM

Roger said:

Hello All,

Please can anyone help me to learn the device driver writing in VC# or VC++ and i am new comer in this field.

I know VC#.net and have little knowledge about VC++

anyone help me asap.

Thanks

Anand

# December 20, 2008 4:50 AM

vanDooren said:

Sorry. Afaik you can't write device drivers in C#.

You have to use C or C++.

have a look at my codeproject articles (links above) to get you started. My articles also have links to the best places to get help if you are stuck.

# December 20, 2008 3:45 PM

David said:

Thanks loads  - this is exactly what i needed - short & sweet ! keep it up!

# December 22, 2008 12:48 PM

dahlia said:

This worked!! For the last year I have been trying to figure out some way to use the software since I did the 2.0 update (which then took away my ability to sync my zune with my computer because of its lack of internet... little side note they didn't tell anyone) and had been unable to figure anything out that didn't involve hacking into parts of my computer that I would rather not do. I wasn't sure if your suggestion was going to work, but it did! I did get an error message after following your steps, saying I was missing a particular .dll file, but all I had to do to fix that was download and install the microsoft netscape 2.0 to correct that issue and then everything worked the way it was meant to. Thanks so much!!

# December 22, 2008 2:05 PM

vanDooren said:

Thx. You're welcome

# December 22, 2008 3:08 PM

vanDooren said:

What you can do (I think I did this once to see how it worked) is to have your DLL create an invisible window, and a message pump that handles those messages. Whenever a message arrives that you are interested it, execute a callback function or set a status flag or something like that.

This is much simpler, and saves you the problems associated with hook functions that you already experienced. It is also much easier to debug.

# December 23, 2008 1:27 AM

freddie s. scranton pa. said:

my girlfriend got the Zune and it's horrible.

she could have gotten something better for the fraction

of the price.

# December 29, 2008 4:34 AM

vanDooren said:

That is very possible. I really like my zune. The sound quality is awsome and the 8GB storage is nice. But if you just want a no-frills MP3 player, it's overkill.

# December 30, 2008 1:40 AM

Cluebat-man to the rescue said:

Last week I had a very peculiar problem with the Terminal Server licensing. I couldn't connect to

# January 6, 2009 12:52 AM

vn said:

i need to pass the below structure from labVIEW to C DLL Can any one help me out .....is thr an option to pass without writting a wrapper?PL do help .....

typedef struct

{

unsigned long NumOfParams;

SCONFIG *ConfigPtr;

} SCONFIG_LIST

typedef struct

{

unsigned long Parameter;

unsigned long Value;

} SCONFIG

pl do help me....

# January 7, 2009 6:47 AM

vn said:

i need to pass the below structure from labVIEW to C DLL Can you help me out in this....is there an option to pass without writting a wrapper?or could how could you pass these kinda structures in labVIEW.

typedef struct

{

unsigned long NumOfParams;

SCONFIG *ConfigPtr;

} SCONFIG_LIST

typedef struct

{

unsigned long Parameter;

unsigned long Value;

} SCONFIG

kindly help me

# January 7, 2009 6:58 AM

Ramesh Marikhu said:

Thanks for the concise explanation, great work.

# January 8, 2009 12:47 AM

David said:

I know this is a VC++ forum, but do you know if the same code will work in Java?

# January 10, 2009 12:42 AM

vanDooren said:

This code should work the same in Java as C++, or any other language. There is one thing that you need to check into, and that is automatic optimization.

I use the keyword 'volatile' to make sure that the compiler doesn't move the reads / writes to the indices.

I don't know the equivalent in java. You'll have to figure that out yourself, or you could disable optimization for that specific class so that the compiler doesn't do something silly.

# January 10, 2009 6:02 AM

Kerberos Query | keyongtech said:

Pingback from  Kerberos Query | keyongtech

# January 18, 2009 11:32 AM

Linear Actuators said:

Hi I followed the link, I was looking for engineering blogs. Thanks for the share.

# January 19, 2009 4:17 AM

Cluebat-man to the rescue said:

I am currently working on some projects where I have to program a DCOM server. There are several reasons

# January 20, 2009 5:00 AM

aufgeben und nach C#? | hilpers said:

Pingback from  aufgeben und nach C#? | hilpers

# January 20, 2009 10:27 AM

InterestedReader said:

>>> There are several reasons why it has to be DCOM and C++,  instead of e.g. .NET remoting and C#.

I'd be interested in knowing these reasons.

THX

# January 21, 2009 11:29 AM

vanDooren said:

Sure thing.

I work in a pharmaceutical environment where I manage the process control servers. I also write apps to automate things to make my life easier.

Most of the servers have the process control software installed. The vendor of that software has very strict 'rules' about what we can and can not install on those computers.

Of course they are our computers, but the vendor only provides support if the systems are all accepted configurations.

With the current software version, that means all servers are Windows 2003 RTM (no SP) and .NET 1.1

.NET 1.1 is less than ideal for interacting with Active Directory. And I cannot install .NET 2.0 because that will make those computers unsupported.

So .NET is out. And if .NET is out, only native options remain, which is C++

I also need to allow automation engineers to do things for which I cannot give them privileges.

So I need to work with a client server technology that allows me to have code invoked by only certain people, which then runs at a higher privilege level. DCOM is ideal for this.

.NET can probably do this too, but It would involve custom configuration on those servers, and I cannot do that because of the support issues.

If I use static linking with C++, I have no need of runtimes, and if I just delete the exe from the server, it is gone and the server is back to its original state.

And the apps I write now are tiny apps that perform a specific task on demand. Startup times need to be as short as possible, just to limit the annoyance factor. The client apps are small dialog boxes which start the COM server. And MFC dialog applications are virtually instant on, while ,NET GUI apps take noticeably longer to load.

That's about it in a nutshell.

# January 21, 2009 1:42 PM

InterestedReader said:

Thank you for the detailed explanation. That does make sense.

I'm looking forward to read the other articles in this series.

# January 21, 2009 4:52 PM

Addressing Physical memory in XP using C++ | keyongtech said:

Pingback from  Addressing Physical memory in XP using C++ | keyongtech

# January 21, 2009 8:45 PM

how do static import libraries actually work? | keyongtech said:

Pingback from  how do static import libraries actually work? | keyongtech

# January 22, 2009 2:40 AM

Semi-newbie question on Visual C++ vs. Visual C# | keyongtech said:

Pingback from  Semi-newbie question on Visual C++ vs. Visual C# | keyongtech

# January 22, 2009 3:08 AM

Andreas said:

Another way the code could be unsafe, that you don't mention in your article, is if a thread is interrupted during the update of the read or write pointer. Sure, you claim in another comment that "32 bit reads and writes are atomic" but, of course, that statement does not generally hold true.

# January 26, 2009 4:11 PM

vanDooren said:

My comment was in the context of that reply.

If you look at the code, you see that I specify the indices to be int. And int is defined to be the natural integer size on a given platform. On a 16 bit platform it is 16 bits. On a 32 bit platform it is 32 bits, and on a 64 bit platform it can be 32 or 64, depending on architecture.

But in each of those cases, int will be written atomically because it is the natural integer size, and it is one of the few certainties you can have when programming on any platform.

so even if you could find a platform that does not follow this rule (don't think it exists) you can hardly claim that my statement is 'generally not true'

# January 27, 2009 1:25 AM

Robert said:

hehe I did that but luckily on a subset of users :)

# January 27, 2009 5:34 PM

Andreas said:

Int is also defined to be >= 16 bits, so they are seldom atomic on 8-bit platforms. And the answer is yes, C++ is used there too... :)

There is no guarantee what-so-ever that int is always atomic. Even on 32-bit CPUs the memory subsystem may be limited to 16-bit, requiring two bus cycles for int access, possibly breaking atomicity. Unaligned accesses may also be non-atomic.

Ok, on x86 with MSVC++ it may be a valid assumption, I don't know. But the vast majority of computer systems in the world does not belong to that specific category, hence the 'not generally true'. For sure, the C++ standard doesn't give any such guarantees, and code like this that depend on implementation specific details should come with warning labels like the one you already have for hardware reordering.

C++0x will address the concurrency issues, probably because this type of synchronization just cannot be done portably and safely in plain C++. (From what I understand, C++0x will also make your method undefined behavior?)

# January 27, 2009 5:47 PM

vanDooren said:

Let me put it a different way then. On a platform that is at least 16 bit, and with an architecture containing the same 'bittiness' :) throughout the entire platform, this should be safe.

I should probably have qualified this up front.

It's just that -when talking about computers with a keyboard and monitor- I don't consider 8 bit controllers, or whacky 16 bit architectures with an 8 bit memory bus, or things like that

It's true that such things exist, it's just not something I consider the norm. Probably because I don't deal with those.

# January 28, 2009 2:29 AM

Majkara said:

What you need to do is a test case and prove it.. once you do ( and I doubt you will write one that will not fail every 7 hours or less)..

I agree they didn't read on single writer, single reader..

But now, you have to ask yourself, where is the proof? You might be just lucky and you are.

People already pointed out to you, you MUST have barriers for it to work. You didn't listen there at all.

Also, volatile is not the way to do it, and volatile on array is just nonsense.

# February 8, 2009 7:07 AM

vanDooren said:

This code is a simplification of production code used in real-time spacecraft ground systems.

Bascially I ripped out the event notifications, and some other stuff that is not to the point.

Just giving a test app is not going to prove anything, because you can't prove threadsafeness with an example. You can only do so by formally working your way through the code.

But when I was debugging a system for which I was the lead developer, I did a lot of testing and analysis.

One of the things I did was to host a bunch of these queues in a process (they acted as message delivery workers) and then have a dozen processes running in parallel, each posting bursts of messages in their dedicated queues, which were read by the scheduler.

The messages were written in short bursts, at random time intervals. This ran on a dual CPU machine for 24 hours, resulting in billions of messages transmitted and received, all accounted for and all in perfect condition.

The reason I didn't post that test code is that

a) it didn't belong to me

b) it ran on unix

c) it was highly application specific (names etc)

Memory barriers are only needed on platforms that perform reordering (which was mentioned) but x386 and x64 do not.

the only thing you need is volatile, to make sure the compiler doesn't operate under the assumption that values remain the same between instructions.

Using volatile on arrays is not nonsense btw:

msdn.microsoft.com/.../145yc477(VS.80).aspx

In C, arrays and pointers are treated the same.

# February 8, 2009 4:10 PM

Robajz said:

Hi, so I wonder, where would we see 8bits nowadays? I can imagine some embedded applications or mobile phones or ...? And what is the solution for those? Just locks?

I was thinking you could actually use this pattern and still get to the multiple readers/writers by simply implementing joins and splits:

Multiple writers:

Writer thread A writes to QueueA

Writer thread B writes to QueueB

Join thread J reads QueueA and QueueB and writes to QueueX

Reader thread X reads QueueX

Multiple readers:

Writer thread X writes to QueueX

Split thread S reads QueueX and writes to QueueA and QueueB

Reader thread A reads QueueA

Reader thread B reads QueueB

You keep the 1 to 1 model....

# February 19, 2009 4:07 AM

vanDooren said:

Exactly. This is how the system core of the application I mentioned operates.

The system core acts as a sort of telephone exchange. the clients send messages to the core, and indicate in the header which recipient the message should go to.

This does have the overhead of having to copy the message a number of times, but when working with short messages (128 bytes I think) it is negligable compared to the context switching that goes on to activate / deactivate a process or thread.

But even on a single CPU P4 I could a throughput of 40000 messages going from one process to another, being processed and acknowledged.

On a hyper threaded dual Xeon CPU system, this number ran into the millions.

technically, it is also possible to make a no-lock multi reader single writer.

In that case, each reader and has their own index. The message array remains the same.

An element is only marked 'free' if it was read by all readers. The reader indices may changes even if the writer is looking at it, but this is not an issue because the reader will only change the index AFTER it has read. Meaning, that worst case, the writer sees no more room, even if there is. But an element will never be overwritten.

# February 19, 2009 5:24 AM

Kyo said:

I have a laptop and a desktop, but I could only get my laptop to install the software at a coffee shop. I can't wait to see if this works, because it was very short-sighted and annoying of microsoft to make their software this way.

# February 20, 2009 10:19 PM

Alfred said:

Thx very helpful....

Though i dont know exaclty the real problem, but since the kdc error keep appearing in event viewer, so i did like you wrote here...

# February 26, 2009 8:33 AM

Daniel Anderson said:

this line:

LPOLESTR strings[] = {L"One", L"Two", L"Three", NULL};

should be replaced by

LPOLESTR strings[] = {L"One", L"Two", L"Three"};

the C/C++ guarantee that one past the last element of an array will be a valid value, you will not be able to dereference it, but you will be able to compare to it

so adding a NULL  to your array is useless.

apart from that, nice introductory article.

# February 27, 2009 1:09 PM

Daniel Anderson said:

I worked at Matrox 20 years ago and we had a similar producer/consumer fifo for interface between a graphic card  and the PC. There is one assumption (for intel platform) that you do not mention: the integer must be aligned on a 32 bit boundary. if you do not do it then the read or write will not be atomic and you will have problems between the producer and the consumer thread.

if you force the indexes to be in memory locations not divisible by 4, something like:

#pragma pack(1)

struct fifoInfo

{

char something;

int readIndex;

int writeIndex;

volatile T m_Data[Size]

};

the read & write index will not be updated atomicly. unless the latest intel architecture has change tremendously.

# February 27, 2009 2:22 PM

Allen said:

I had this same event happen.

I was running Server 2008 for 120 days and when the terminal license server expired, it was offline for a day or so. I reset the actual server and now I have been running Terminal License Free for about 3 months.

I have purchased 20 extra license just in case it flips out on one day and tries connecting to the terminal licensing server, but for now, its alll good.

Strange.

# February 27, 2009 7:14 PM

vanDooren said:

I'll check it out to see if I can find a mention of that in the C++ reference. I doubt this is true, because I would have noticed it in e.g the size of a struct with embedded arrays. It could cause other problems as well (the dummy unconstructed elements)

I'll post a follow up with the answer, regardless if I am right or wrong. Thanks for commenting

# February 28, 2009 4:20 AM

vanDooren said:

Aieee....

Yes you are right. I know this is true. The default compiler settings of gcc and vc make this a non issue in normal circumstances, but you are right that changing the default packing size can cause serious problems if the integers are not naturally alligned.

I should have though to mention it. I'll see if I can update my article. Thanks.

# February 28, 2009 4:34 AM

Daniel Anderson said:

you can doubt as much as you can, read TC++PL 3rd edition, chapter 5, section 3 state:

"Taking a pointer to the element one beyond the end of an array is guaranteed to work."

see also 2.7.2

I've often seen this error in C++ samples from microsoft. which probably explains why this error is so frequent.

Of course dereferencing that pointer one past the last element is a big no-no.

# March 2, 2009 9:54 AM

vanDooren said:

Sorry we were talking about 2 different things.

I just read that section in the C++PL, and it says what you mean, but that is not why I had the NULL. I should perhaps rephrase that part.

I know I can simply do something like end=begin + size, for example, and the pointer will work for the sake of arithmetic.

But there is nothing there to dereference. I misinterpreted your earlier remark to mean that you thought that C++ inserted a dummy element that could be dereferenced. My bad.

Anyway, yes I know the address value will be valid.

But I have learned the hard way never to pass pointers to other components without making sure that there is something valid there.

I have worked with enough programmers who had problems with the concepts that pointers had to point TO something. I learned not to give out pointers on the understandig that it was for arithmetic only.

Yes, they should have known these things. But it's not like I had any say in whom I worked with, since I was the outside consultant.

And if their component causes trouble, they are to blame. But unfortunately, I was the lead programmer who had to fly to France to figure out why this very expensive satelite testing system was crashing regularly.

So learned to apply 'defensive driving' skills to programming. Adding the NULL doesn't have any significant cost, but if someone else screws up by making a simple mistake, using my pointer, it doesn't take down the system.

# March 2, 2009 5:25 PM

Andy said:

Thanks!  This helped me solve a problem i was having.

# March 3, 2009 2:46 PM

Frank Young said:

Hi vanDooren,

Your description here is valuable to me since I'm having trouble to unload the dll that contains some Detours code. And Your solution 3) on Dec. 17 exactly matches my requirement.

However, I don't know how to obtain the function pointer in my DLL module which unhooks the system calls.  

Existing sample codes I found leverage GetProcAddress() to obtain a well-known function pointer (which is assumed to be the same across different processes) in Kernel32.dll (like LoadLibrary). But the function in my DLL is not shared across process. Even it's shared, different process may load it onto different memory address.

Are you aware of any solution to this?

Thank you very much!

Frank

# March 3, 2009 10:37 PM

vanDooren said:

what you could do is to export a function in your dll that will perform the unhook function. the hook handle will be stored in a global variable in your dll.

then a second application will inject a thread in the target process that will call that dll function. this should unhook the hook.

to be honest, I never tried this, so there might be a caveat or 2. one thing that could be necessary is to store the hook handle in a shared data segment (using declspec). But if the unhook function executes in the context of the target process, this shouldn't be necessary.

Another thing that could be necessary is to have your thread function call LoadLibary after it is injected to load the dll, and then use getprocaddress to load the function pointer for the DLL function that will perform the unhook.

# March 4, 2009 12:33 PM

Frank Young said:

Hi vanDooren,

Thanks.

It turns out I have to inject the binary executable code to the remote thread and have that code to call the GetProcAddress("MyExportedFunc") to obtain the function pointer.

A weird thing is: when I WriteProcessMemory to copy the &LocalThread to RemoteAddress, the copied binary isn't exactly the same as the original code. There are 4bit error in the 5th byte

I verify this by reading the runtime memory address in the processes, respectively. And I also memcpy the binary code to a local buffer and the same error happens.

I guess this may be due to Windows Vista has some protection on reading data from the code segment.

I work around this by declaring a const char[] to maintain the machine code I want to inject and now everything sees ok.

Thank you for your help and hope my experience can be of any use.

Frank

# March 5, 2009 10:41 PM

Frank Young said:

By the way, I believe your idea of injecting second dll to the remote process and having that dll unhook the first one would definitely work.

And it seems easier. I try my current method is just out of curiosity.

# March 5, 2009 10:53 PM

Dilip said:

I am a little late to this party but how in the God's name do I remove this compiler option from the set of compiler switches?  I have migrated my project to VS 2008 and I get that dreaded D9035 warning which says that the /clr:oldSyntax option will be deprecated in a future release.  Ok I get that but how do I remove it?

# March 16, 2009 1:15 PM

vanDooren said:

There are 2 ways that spring to mind. The first is that /clr:oldSyntax is configured under configuration properties ->general->Common Language Runtime Support which could still be set to 'Use common language, old syntax.

The other place where it could be specified would be under configuration properties ->c++->command line

But my money would be on the first option.

NOTE: this can also be specified on a file by file basis.

So first check the project settings. if you don't find it there, have a look for which file generats this problem, and then look at the file settings.

# March 16, 2009 5:19 PM

glass said:

So good article!I have kown lot of  detals about COM,Thanks very much! I think I will read more of your blog articles in the feature !

# April 9, 2009 6:30 AM

Tom said:

What happens if the Task is already running when the Scheduler calls it again.  Is there a configuration setting that allows you to stop the current or delay the new task until the previous one finishes, etc.?

I had the same issue as you and used a program called IsUsedBy mstsoftware.com to determine what program is using the Task.  It showed me I had 17 calls to the program that had been abandoned.  A reboot would have fixed mine as well but I would not have known.

# April 10, 2009 10:28 AM

vanDooren said:

In the scheduled task properties you can specify that the task has to be aborted if it runs for longer than x time. And after that it can be restarted.

Bear in mind that IF the task is aborted, any processes started with it are NOT aborted.

In some cases, the first thing my task does is to kill all processes normally associated with that task to avoid problems. I have one task that starts Excel to produce a report via some macros. And sometimes it fails.

To avoid problems, it kills all Excel processes and instances of teh ActiveX servers that are running.

# April 15, 2009 7:39 AM

Vic said:

Wusthof are excellent choice for kitchen knives, there are many types of Wusthof knife. If you got the right type to cut meat, then Wusthof will be better choice than The Kai. Yes its true there are saying that japan knives are sharper than any other knives in the world, but nowadays choice of sharp knives also available from different brand.

# April 18, 2009 1:34 PM

Angelovic said:

Hi Bruno,

thank you a lot for this useful article.

I'd like to ask you for an expert advice :-)  since I'm not sure about 1 thing. I was considering why elements in the queue need to be volatile.

Proper instruction ordering is still ensured even if queue is not volatile: since m_Read and m_Write are volatile, and they have proper Acquire/Release semantics - the compiler will not reorder memory instructions and so flag is always set after the queue has really been updated.

So it appears to me that the only important reason why the queue is volatile is when there are multiple processors, each one having its own cache. That way reads/writes to the queue are immediately reflected in the shared memory, am I right ?

I'm asking because type T in my case is a class which is not under my control, and compiler insists on having operator= for that class defined as volatile.

And one more question: If I'm right with the above presumption, then even if I do use locks to synchronize access to the queue, it still needs to be volatile to work in multi-processor enviroments. Is this correct... ?

I thank you in advance for your reply,

S. Angelovic

# June 5, 2009 5:21 AM

Sauvaget said:

Hi,

To have atomic manipulation on basic types like integers (readIndex and writeIndex ) you can use (only under linux with the pthread library) the __sync_fetch_and_add() function to increment it for example.

I guess you have to align your basic type.

Theses atomic functions are made to be used with basic types, not structures.

Nicolas.

# June 11, 2009 1:41 AM

noko said:

I bought a zune and found that microsoft made it so that the zune software can't be run on a linux-based operating system with wine. this annoyed me, but i prepared a desktop computer with XP installed so that i can get the zune software going on it. come to find out, i needed an internet connection. i was pissed. thanks for the tutorial though. helped me out a lot!

# June 15, 2009 2:38 PM

bullet said:

From what I read your "right way" shouldn't be done this way. The MSDN says calling LoadLibrary() in a DLLMain() should not be done which you do. I don't even understand why this doesn't get a lock-up, you call LoadLibrary() on a DLL that hasn't finished loading. From my understanding this should go into an infinite loop. I didn't read the referenced article on the NT Loader though. I assume it tags loaded-but-not-intialized DLLs?

# June 28, 2009 1:13 PM

calcium said:

Parallel programming is used specifically to serve working software developers, not just computer scientists. It is a complete, highly accessible pattern language that will help any experienced developer "think parallel"-and start writing effective parallel code almost immediately. Instead of formal theory, it deliver proven solutions to the challenges faced by parallel programmers, and pragmatic guidance for using today's parallel APIs in the real world.

# November 20, 2009 7:20 AM

forexstrat_egy said:

I am definitely bookmarking this page and sharing it with my friends.

:)

# December 4, 2009 10:08 AM