VB6 Logic Bomb



  • @Sutherlands said:

    I would say your example is not a "null-object" but a "do-nothing-object". Which is perfectly valid.
     

    Which is also perfectly validly called a "null object (pattern)"

    "Instead of using a null reference to convey absence of an object (for instance, a non-existent customer), one uses an object which implements the expected interface, but whose method body is empty. The advantage of this approach over a working default implementation is that a Null Object is very predictable and has no side effects: it does nothing."

     

    also, sorry for my previous unnecessarily aggresive comment, i just couldn't understand how you can't understand the point of a null object and persist on trying (poorly) to point out its unnecessity and performance ineffectivity. I still don't understand it, but i acknowledge that my reaction was dumb.



  • @SEMI-HYBRID code said:

    Which is also perfectly validly called a "null object (pattern)"

    "Instead of using a null reference
    to convey absence of an object (for instance, a non-existent customer),
    one uses an object which implements the expected interface, but whose
    method body is empty. The advantage of this approach over a working
    default implementation is that a Null Object is very predictable and has
    no side effects: it does nothing."

    Then maybe my only thing against it is the name.  If you want an object that specifically does nothing, (like not logging, or providing a straight toString) provide a "null object".  I would never call it that, though.  If you're providing an object no matter what, just to avoid a null check... that's pretty crazy.

    @SEMI-HYBRID code said:

    performance ineffectivity

    My point was not that it performs poorly, it doesn't.  My point was that it doesn't perform better than a null check, so don't do it "for performance," which was something that was implied to have been done.

    @Xyro said:

    But then your code has to check for validity (even if it's just a boolean) every single time the desired action is asked to be performed



  • @Darthon said:

    I'm updating a VB6 application to work with MySQL and SQLite. Now VB6 does have a concept of NULL but throws a hissy fit if you attempt to assign a NULL value to any of the basic data types (int, float, string). To avoid this issue, as part of writing a DLL in C that handles all of the database communication, I'm trapping the NULL values and converting them to a more benign value that VB likes. Int and Float columns get set to zero, string columns to a blank string.

    VB uses the BSTR construct for strings, so I allocate a zero length BSTR and return that instead of NULL. Works fine in Vista & Win7 but crashes in WinXP. After much swearing and hair pulling, I find a note tucked into one of the knowledge base articles saying that by convention, a NULL BSTR pointer must be treated as a blank string. Problem fixed.

    So, because VB6 has issues with NULL values, I must return a NULL.

     

     

    There are always workarounds in VB6.  Here are two things that may help you

     

    1)  There are the undocumented functions "StrPtr" and "ObjPtr". 

    2)  You could define the interfacing variables using Variant data types.  This is fairly common when data is passed between DLLs.

     

    My question is why you'd need a C Dll to allow VB6 to work with MySQL or SQLLite?  Vb6 can connect to these data sources using ADO.  What  advantage is there to using C?  Or does the C Dll already exist?

     



  • @Salami said:

    There are always workarounds in VB6.  Here are two things that may help you

     

    1)  There are the undocumented functions "StrPtr" and "ObjPtr". 

    2)  You could define the interfacing variables using Variant data types.  This is fairly common when data is passed between DLLs.

     

    My question is why you'd need a C Dll to allow VB6 to work with MySQL or SQLLite?  Vb6 can connect to these data sources using ADO.  What  advantage is there to using C?  Or does the C Dll already exist?

     

    Yep, there's always more than one way to skin a cat. StrPtr and ObjPtr are useful when you're passing information into a DLL, but in this instance, the problem is getting data back out of the DLL. Other folks have pointed out that I could easily use IfNull() but I feel that:

    <font size=+1>If IfNull(result!name) Then name$ = "" Else name$ = result!name EndIf</font>

    is really ugly code and way too much typing. Better to catch the NULL values inside the DLL and set them to blank/zero values which is what I was going to do when IfNull() returned True anyhow.

    As for why I'm writing my own DLL instead of using the ADO layer? Speed, pure and simple. My first pass at writing a DB transfer routine to dump a 200Mb database from MySQL into SQLite took 2 hours to run because of the ADO/ODBC layer. With my DLL, it take 4 minutes. VB is great for designing user interfaces but when I want to move a lot of data around fast, I'm headed back to good ol' C. Both SQLite and MySQL have C APIs available, so all I needed to write was a couple of hundred lines of glue code.



  • @Darthon said:

    @Salami said:

    There are always workarounds in VB6.  Here are two things that may help you

     

    1)  There are the undocumented functions "StrPtr" and "ObjPtr". 

    2)  You could define the interfacing variables using Variant data types.  This is fairly common when data is passed between DLLs.

     

    My question is why you'd need a C Dll to allow VB6 to work with MySQL or SQLLite?  Vb6 can connect to these data sources using ADO.  What  advantage is there to using C?  Or does the C Dll already exist?

     

    Yep, there's always more than one way to skin a cat. StrPtr and ObjPtr are useful when you're passing information into a DLL, but in this instance, the problem is getting data back out of the DLL. Other folks have pointed out that I could easily use IfNull() but I feel that:

    <font size="+1">If IfNull(result!name) Then name$ = "" Else name$ = result!name EndIf</font>

    is really ugly code and way too much typing. Better to catch the NULL values inside the DLL and set them to blank/zero values which is what I was going to do when IfNull() returned True anyhow.

    As for why I'm writing my own DLL instead of using the ADO layer? Speed, pure and simple. My first pass at writing a DB transfer routine to dump a 200Mb database from MySQL into SQLite took 2 hours to run because of the ADO/ODBC layer. With my DLL, it take 4 minutes. VB is great for designing user interfaces but when I want to move a lot of data around fast, I'm headed back to good ol' C. Both SQLite and MySQL have C APIs available, so all I needed to write was a couple of hundred lines of glue code.

     

     

     Instead of this

    <font size="+1">If IfNull(result!name) Then name$ = "" Else name$ = result!name EndIf</font>

    you can do this

    <font size="+1">name$ = result!name & ""</font>

     

    I would have thought the limiting factor for moving data between databases would be how fast the receiving database can insert the data.  C should be 10 or 20% faster than VB6 in string manipulation. 

     


     



  • @Salami said:

    Instead of this

    <font size="+1">If IfNull(result!name) Then name$ = "" Else name$ = result!name EndIf</font>

    you can do this

    <font size="+1">name$ = result!name & ""</font>

     

    I would have thought the limiting factor for moving data between databases would be how fast the receiving database can insert the data.  C should be 10 or 20% faster than VB6 in string manipulation. 

     


     

    That works fine for strings (which surprised me frankly) but just shifts the run-time errors to integers & floats.

    C is probably twice as fast as VB when handling strings, I'd have to benchmark it to be certain. However, the big time sink is handing the information up and down the stack through ADO/ODBC. Think about all the detailed crap that "magically" happens when you're using an abstraction layer like ADO.

    When I pull information out of MySQL, at some point the ODBC code is making the same C API call that my DLL is. From there, there's error checking, allocating memory for the BSTR, converting 8bit chars to 16bit wide chars, creating the collection, insert string into collection, index all the column names, etc. At this point the string is back in VB land, where run-time error checking kicks in, need to quote the string, allocating more BSTRs to build SQL statement (if you're not using prepared statements) and then make the ADO call to pass it back to SQLite. Going back down into ADO/DLL requires the BSTR to be converted back into 8bit chars, which requires at least one more memory allocation before ADO makes the same SQLite insert API call that my DLL does. Repeat the above for 20+ columns and several million rows and it certainly adds up.

    By writing my own DLL, I can eliminate all of that pointless bit twiddling. MySQL API hands me a char pointer to string, I hand that pointer to SQLite's prepared statement insert API. Done.

    Writing ADO code is certainly more convenient than all the hoops I'm jumping through with my own DLL interface code. That's why my first pass was written with ADO. However once I hit those performance problems (and a bug in ODBC as well) I needed to roll my own code to speed things up.



  • @Sutherlands said:

    My point was not that it performs poorly, it doesn't.  My point was that it doesn't perform better than a null check, so don't do it "for performance," which was something that was implied to have been done.

    I implied wrongly. The (is it null yet?) overhead (is it null yet?) is (is it null yet?) entirely (is it null yet?) mental, (is it null yet?) which (is it null yet?) I (is it null yet?) believe (is it null yet?) is (is it null yet?) non-trivial.

    In any case, I like Darthon's story. His is a much better tact to this overripe thread.

    Edit: Oh wait, he's the OP, isn't he? heh. Well, how often do we see a thread re-railment around here?



  • @Darthon said:

    @Salami said:

    Instead of this

    <font size="+1">If IfNull(result!name) Then name$ = "" Else name$ = result!name EndIf</font>

    you can do this

    <font size="+1">name$ = result!name & ""</font>

     

    I would have thought the limiting factor for moving data between databases would be how fast the receiving database can insert the data.  C should be 10 or 20% faster than VB6 in string manipulation. 

     


     

    That works fine for strings (which surprised me frankly) but just shifts the run-time errors to integers & floats.

    C is probably twice as fast as VB when handling strings, I'd have to benchmark it to be certain. However, the big time sink is handing the information up and down the stack through ADO/ODBC. Think about all the detailed crap that "magically" happens when you're using an abstraction layer like ADO.

    When I pull information out of MySQL, at some point the ODBC code is making the same C API call that my DLL is. From there, there's error checking, allocating memory for the BSTR, converting 8bit chars to 16bit wide chars, creating the collection, insert string into collection, index all the column names, etc. At this point the string is back in VB land, where run-time error checking kicks in, need to quote the string, allocating more BSTRs to build SQL statement (if you're not using prepared statements) and then make the ADO call to pass it back to SQLite. Going back down into ADO/DLL requires the BSTR to be converted back into 8bit chars, which requires at least one more memory allocation before ADO makes the same SQLite insert API call that my DLL does. Repeat the above for 20+ columns and several million rows and it certainly adds up.

    By writing my own DLL, I can eliminate all of that pointless bit twiddling. MySQL API hands me a char pointer to string, I hand that pointer to SQLite's prepared statement insert API. Done.

    Writing ADO code is certainly more convenient than all the hoops I'm jumping through with my own DLL interface code. That's why my first pass was written with ADO. However once I hit those performance problems (and a bug in ODBC as well) I needed to roll my own code to speed things up.

     

    The way to do this is to use the GetString method on the ADO Recordset to get all the data from a single SQL query into a single string.  Then do the string manipulation  and either write the SQL Insert statements to a file or execute them on the fly.  VB6 will never be as fast as C, but there is always a price to pay for using a C DLL when you could do it natively in VB6.  

     


     


Log in to reply