Hacker News new | ask | show | jobs
by jasode 592 days ago
>Today, I feel .NET is overly complex and much harder for beginners compared to the old days.

I often see similar statements like that but that's not what I experienced. I did IBM DOS BASIC, MS GWBASIC and worked on VB3 to VB6 at a corporate jobs.

The C#/NET workflow feels much the same rapid-development simplicity as classic VB6: Drag some GUI components like text boxes and buttons onto a form, code the controls' event handlers, build the exe.

In contrast, the examples of GUI dev kits that had more complex language syntax and build steps than VB6 were original Apple iOS Objective-C, C++ Qt, Java AWT.

Sure, C# is a bigger language than Visual Basic in VB6 but C# also does a lot more. E.g. in VB6, it didn't even have a built-in way to check the existence of a file. Instead, you had to declare a "win32" API monstrosity such as :

  VB6:  
  Private Declare Function OpenFile Lib "kernel32" ByVal lpFileName As String, lpReOpenBuff As OFSTRUCT, ByVal wStyle As Long) As Long
  Function FileExists(FileName As String) As Integer
  Dim RetCode As Integer
  Dim OpenFileStructure As OFSTRUCT
  Const OF_EXIST = &H4000
  Const FILE_NOT_FOUND = 2
  RetCode = OpenFile(FileName$, OpenFileStructure, OF_EXIST)
  FileExists = (not OpenFileStructure.nErrCode = FILE_NOT_FOUND)

  C#:  
  File.Exists()
In many ways, VB6 being "simple" means it created a ton of extra complexity for the programmer to do basic tasks. Another example is that VB6 didn't include a datagrid. You had to buy 3rd-party VBX/OCX controls for that. C# WinForms includes a datagrid.

EDIT reply to: >most definitely open files in VB, and therefore the "File.Exists()" function would at worse be comprised of a exception handler (on error ...) and a open call,

From my memory the "pure" VB6 way checking existence of a file by opening a file with error handler had issues (other processes using exclusive access and/or other issues) causing false-negatives or false-positives. Therefore, the recommended way back in 1990s was the win32 api declaration using OF_EXIST flag. It looks convoluted but it was more reliable.

5 comments

You chose a very poor example, since you can most definitely open files in VB, and therefore the "File.Exists()" function would at worse be comprised of a exception handler (on error ...) and a open call, as in many other languages. No need to import OpenFile for sure.
I believe some people used that monster version because VB had simple libraries that didn't allow many options. Anything more complex required you to go down the Win32 rabbit hole. And that was the main weakness of VB, it was such a simple language that you didn't have many options to do something more complex than going straight to Win32.
It is a very different thing to claim "the language doesn't allow simple things such as checking for a file's existence" (which is false, the language allows many ways to do so) than to claim "the language doesn't have many options, but allows you to interop with the entire win32 API if you need to". The first is a valid if only false complain; the second reads to me not as a complain, but as a feature.
>to claim "the language doesn't allow simple things such as checking for a file's existence" (which is false,

That's not what I wrote. I said there's no _built-in_ way to check existence of the file. A reasonable interpretation of "built-in" is for an obvious named function such as "File.exists()" _without_ cobbling together extra workarounds and hacks -- whether those workarounds include re-purposing other VB built-in functions with On Error ... or using Win32 interop.

> in VB, [...] would at worse be comprised of a exception handler (on error ...)

That's an example of what I meant of not being built-in to VB. I should have been more clear. We were using different meanings of "builtin".

In any case, the following "pure" VB code to check for file existence is not found on Google or Bing search but I still have it from my 1995 archives and copied it here. It has many lines of code (instead of a simple "one-liner") because it tries to cover all the edge cases. And yet with all that defensive code, there's still a hidden timebomb of a bug in it. Can anyone spot it? The code is unmodified from Visual Basic Programmer's Journal. In contrast, the alternative using Win32 interop with OF_EXISTS doesn't have the bug.

  Dim strTestFilename As String
  strFilename = Trim(strFilename)

  ' To avoid problem with known bug, don't pass root directory to
  ' the Name function
    Select Case Right$(strFilename, 1)
        Case "\": strExistsFilename = "Root": Exit Function
        Case "\.": strExistsFilename = "Root": Exit Function
        Case "\..": strExistsFilename = "Root": Exit Function
    End Select

    On Local Error Resume Next      ' Enable error trapping for Name function
    Name strFilename As strFilename ' A trick to see if filename/path is good

  ' Now check the resulting err code of the Name function
    Select Case Err
        Case 5  ' Illegal function call
        Case 53 ' File not found
        Case 58         ' File exists (maybe)
            strTestFilename = Dir$(strFilename)
            If Len(strTestFilename) Then
                strExistsFilename = ""      ' It's a file
            Exit Function
            Else
                strExistsFilename = "Dir"   ' It's a directory
                Exit Function
            End If
        Case 64 ' Bad filename
        Case 68 ' Device unavailable
        Case 71 ' Disk not ready
        Case 75 ' Access denied
        Case 76 ' Path not found
    End Select
My point was that C# builtin File.Exists() is a lot simpler than that.
So, what is so wrong about simply calling FileLen() to check if the file exists, or if you also want to distinguish between Files/Directories using GetAttr to check the directory bit ? Why do you have to go all the way to this monster of code which even hardcodes lists of errors (why do you care which error it is?) ?

Your code sample renames the file, then globs anyway (when dir$ by itself can also be used to check existence). It looks too convoluted, and I cannot imagine why it needs to be so.

What is exactly the corner case that you want to avoid -- and if such case really exists, why are you so sure the File.Exists() implementation would not fall into the same pit trap, considering it also simply uses the metadata?

Disclaimer: I'm only familiar with VBA, not VB itself. But these file metadata functions definitely already exist in 95's VB5. (EDIT: Actually 97's. Maybe the problem is these functions did not exist in earlier VBs? But I would find it hard to imagine you could not check a file's size).

The sample code smells even in the trivial-case checks. E.g. Right$(_, 1) will only return a 1-char string at most, yet it tries to compare it with 2, 3 len strings. I didn't check any further than that.

To elaborate.. just what is wrong with simply doing the following?

    Function FileExists(Filename) As Boolean
      On Error Resume Next
      FileExists = (GetAttr(Filename) And vbDirectory) = 0
    End Function
It's not 2 lines, but it's 4, including function header. All functions used are available at least since 16-bit VB3. Is this what you call "a workaround and a hack?". It's practically the same thing .NET is doing for File.Exists() (or at least the current one -- dunno what .NET 1 was doing), so I cannot think of any gotchas that would affect this but not File.Exists.

Another way, use Dir by itself. This is even mentioned in the VB docs...

    FileExists = Dir(Filename) <> ""
3 lines. It also ignores directories. Sure, this has problems with wildcards, but so does your example, and that's another oneliner to fix, using Replace$ (as wildcards are not legal characters in filenames in win32, either way). And if Dir("\") returns true for some reason, I guess that's a runtime bug, which is pretty valid criticism, even if a bit of a corner case (who really wants to check if "\" exists?). I would prefer the first version anyway.

I still stand by my original point that this is a very poor example, since there are a million ways check for a file's existence, many using only builtin functions of the language. You can certainly find nigh-overcomplicated ways to do so, apparently even on books, but ... why? Not a fan of cargo-culting like this; it tends to create Cherteston's fences.

Many languages also lack a direct "File_Exists" function (e.g. Lua comes to mind) since it's about the most trivial thing to implement (and a magnet for TOCTOU issues). Or even if they do come with such function, it does not distinguish directories from files (e.g. Tcl). I hardly think the lack of a "file_exists" function discriminates anything in language design, modern or otherwise.

But then you may have actually opened the file.
But if you rely on these subtleties this is hardly a "basic feature" that the language is lacking. In fact, I imagine File.Exists()/OF_EXISTS also opens the file (then immediately closes it).
It better not!

If you actually open a file, you update it's access time, and you interfere with the process that should actually open the file. Those both break perfectly simple business logic.

The documentation for OF_EXISTS literally says it will open then close it. I leave to your imagination whether this updates the (very poorly defined) access time or not, but for sure you "interfere" with other processes...

If you want to get the access time, why don't you just query the access time? Which is likely the only thing almost universally guaranteed not to change it... And I'm sure VB6 has an API for that, considering that VBA had a global API for it (FileDateTime).

(Which is, btw, yet another way to one-line check if a file exists).

Did you confuse the file with it's containing directory?

The way you probably did in your now-deleted comment claiming that stat() updated atime?

Citation needed. https://learn.microsoft.com/en-us/dotnet/api/system.io.file.... doesn't warn of any such thing.

But then the only way to make sure that you can open a file is to actually open it.
You don't use exists and then open a file. If you actually want to open a file, you open it or fail. You don't check if it's ok and then do it, you do it and then check if it failed.

exists followed by open is a pointless exists because anything can happen in between.

Conversely, if you are neither the producer nor consumer of the file at this time, you don't want to actually open it just as a way to stat it, because actually opening it updates it's access time and interferes with the actual consumer(s). Even read-only non-exclusive.

You can use it as an improvised lock.
How do you impliment this lock? You use an atomic operation. Which atomic operation? You try to open the file in exclusive mode.

    Public Function Fso() As Object
        Static s_oFso As Object
    
        If s_oFso Is Nothing Then
            Set s_oFso = CreateObject("Scripting.FileSystemObject")
        End If
        Set Fso = s_oFso
    End Function
    
    Fso.FileExists("path")
>CreateObject("Scripting.FileSystemObject")

That's not included by default in Windows 95. (A lot of classic VB6 apps of that era ran on Win95.) In contrast, "kernel32.dll" is always included.

Even for the later Windows 98, using "Scripting.FileSystemObject" as a dependency was fragile and could fail:

https://www.tek-tips.com/threads/forms-do-not-open-in-window...

https://www.vbforums.com/showthread.php?135362-running-vbs-f...

The sub thread this created of people not recognizing this as a problem is pretty interesting.

It's probably one of many ingredients that went into how so many of these apps are so half baked and buggy.

Not just the obvious that the language was aimed at and used by junior programmers and so of course much of the code is not robust in general.

But here is an example of the toolbox missing a tool. You can't really fault people for doing whatever seems natural using the tools they are given.

Not being experienced developers otherwise, they didn't know that something necessessary was missing, and made things that only mostly worked without it.

To be clear they would have often mis-used the tool if it existed too. In this thread people have thought the way you would use exists is to check before doing some actual operation. The inexperienced coder aspect is also true, seperately.

But the combination of a missing tool combined with a language explicitly aimed at users who don't come with their own experience and don't know anything but what the language provides, seems a little extra nasty.

Are you literally calling every commenter of this subthread an inexperienced developer? Seriously?

First, the subthread starts by claiming you cannot check for the existence of a file, which is simply false. You can try to open it, you can try to read metadata from it, you can glob it, etc. all of it using language builtins.

But then you claim that you need to check for a file's existence without actually opening it. If anything, as we people are constantly pointing to you, any code which does it is a code smell at the very least, and very likely wrong. There's a reason TOCTOU is a thing, and why these exist/access APIs usually have huge disclaimers right in the documentation.

But let's entertain the idea. Maybe you really have a crappy IPC handshaking mechanism implemented with temporary files (sorry!). Maybe you want to avoid side-effects of trying to open file (virus scan overhead? network traffic?) . It cannot be that you want to preserve the atime, because that would also be a code smell, seing how undefined the atime is on win32. On win9x, for example, reading metadata updates atime - likely the AV scan, but nonetheless; also, the granularity is only 1 day (welcome to FAT).

The WA offered by GP actually very likely opens the file anyway, so clearly he didn't have this requirement. But anyway, this scenario is no longer a "simple" feature whatsoever, and therefore the original complain loses all weight.

And to top it all, you _still_ can check the existence of a file w/o opening it by just trying to read the metadata, using any of the myriad functions at your disposal, in around two lines of code; no need to interop with Win32 at all. In fact, (trying to) get the file's metadata is exactly how .NET's File.Exist does it.

If you find yourself in a situation where everyone thinks you are trying to do a senseless thing, it is more likely you are, rather than everyone else being junior and inexperienced.

I am calling inexperienced developers inexperienced. Are you literally suggesting that there are no inexperienced developers?

I am also saying that this thread is interesting and that inexperienced developers don't see the problem.

"very likely opens the file anyway" is not an argument.

"code smell" and "very likely wrong" are not arguments.

The reasons you need to stat a file without opening it, or do any other low level operation, are open ended and infinite. It's backwards to try to think up some specific invalid contrived example use case, and then say how you didn't need to do that specific thing that specific way, and so therefor you never needed that facility.

These are all quite bad reasoning and "inexperienced developer smell". If you are experienced and still reasoning like this, that's unfortunate.

I know this is a bit of a first-world problem but back in those days[tm] you just slapped 2-3 DLLs into the exe directory and it worked. Yes, I know there were also different versions for different runtimes, but overall it was a couple of DLLs and fine. Since .net you really need to install the distributable by MS, and every new .net upgrade it's the same. Just 2 days ago I apparently needed the .net 8 runtime and even after installing it the app in question didn't work (without a reboot maybe, I will find out later today).

Overall, strictly as a consumer, C# and .net apps have been mostly great, except the practice (is it a best practice?) that some of them end up somewhere in my home dir's Local dir and not in c:\program files.

This is the worst example you could give. Just add filesystem dll or whatnot from the .net framework as a reference to your project and you can use File.exists from VB.
>Just add filesystem dll or whatnot from the .net framework as a reference to your project

To clarify, "Visual Basic 6" in this thread's title that everybody in this discussion has abbreviated to "VB6" is the "classic VB" from 1998.

There was no publicly released .NET in 1998. That came 4 years later in 2002. Maybe you're thinking of the newer VB.NET.

yes, I misread thanks