1

I am using Visual Studio 2022 on a Windows 10 platform, and have created a function in C to prevent overwriting a file. It makes use of getSuffix() and fileExists(), which are two functions that I had previously created and tested, and work correctly. The function protectWriteFile() should be called before an attempt is made to open a file for writing. The extension argument *ex, should be "hard-coded" in the function call, and if the filename with the extension does not match the extension, a negative value is returned indicating that it is invalid to open the file for writing. A zero return value indicates that the file does not exist and can be created and written to, and a postive return value indicates that the file already exists and should be safe to overwrite.

The function protectWriteFile() is listed as follows:

int protectWriteFile(CCHAR* fn, CCHAR* ex) { char exten[EXTEN_LEN] = { 0 }; int suffixLen = getSuffix(fn, EXTEN_LEN, '.', '\0', exten); if (suffixLen <= 0) return -1; if (strcmp(ex, exten) != 0) return -2; if (!fileExists(fn)) return 0; return 1; } 

In the calling function I have the following:

errno_t fileErr = 0; char logFn[FNAME_LEN] = { 0 }; printf("Type the output log filename: "); scanf_s("%s", logFn, FNAME_LEN); if (protectWriteFile(logFn, "log") < 0) { printf("*** ERROR: An invalid file extionsion that is not \"log\" is provided ***\n"); return 1; } fileErr = fopen_s(&logFp, logFn, "w"); if (fileErr) { printf("*** There is an error when attempting to open the log file ***\n"); return 1; } 

where the file is opened for writing if a non-negative value is returned. This works correctly.

However, I want to be able to test if the file is read-only without opening the file first, so some code should be added after the return 0 in the function, and will also return a negative value if the file is read-only.

Looking at some documentation online, the header <windows.h> should be included, then the following code between return 0 and return 1 in my function should be added:

DWORD fileAttr = GetFileAttributesA(fn); if (fileAttr == INVALID_FILE_ATTRIBUTES) return -3; if (file_attributes & FILE_ATTRIBUTE_READONLY) retun -4; 

This reads the file atrributes and finds if the file has invalid attributes or is read-only, and returns the appropriate negative value.

However in adding the line #include <windows.h> in my header, the compiler generates a large number of errors due to the fact that in my header I have several typdefs, such as

typedef const uint32_t CUINT;

which apparently collide with the typdefs in <windows.h>. I have too much code to be able to change this, so there must be some other solution. My question is, how do I use either the last bit of code listed above, or some equaivalent code on a Windows platform to be able to test a file without first opening it?

10
  • 5
    If you have a problem with namespace collisions due to #include <windows.h>, then I recommend that you put all functions that need #include <windows.h> into a separate .c file. That way, most of your project will be unaffected by you using #include <windows.h>. Commented May 9 at 3:12
  • 4
    in my header I have several typdefs, such as Do not paraphrase the errors, post a few them verbatim from the top if you want you question to be helpful to other users with similar problems. Commented May 9 at 3:30
  • 3
    Even better, provide a minimal reproducible example please. Commented May 9 at 4:34
  • 1
    "I have too much code to be able to change this" - How hard can that really be? If you're working on Windows, sooner or later you'll need to include Windows.h in a header so just stay away from defining types that collides with those you get from Windows.h. You should in most situations use the types already defined by Windows.h instead. Commented May 9 at 4:54
  • 2
    Have you really thought your functionality through? You seem to check for the existence of the file and opening it for writing in separate steps. That leaves a window open for some other process to create the file between the check and the opening. Use "wx" to open the file instead. It will fail if the file already exists or create and open it if it doesn't exist atomically. No need to check before trying to open it. Commented May 9 at 4:59

1 Answer 1

2

Minimize namespace pollution

There is no need to #include <Windows.h> in your entire project, thereby polluting the namespace of your entire project. Only the .c files which use the functions of the Windows API are required to #include <Windows.h>.

In order to minimize namespace pollution (and thereby also minimize the chance of naming conflicts), you can put all functions which need #include <Windows.h> into their own .c file. That way, you only have to #include <Windows.h> in that single file, and all other files will be free of the namespace pollution.

Additionally, you may want to add the line

#define WIN32_LEAN_AND_MEAN 

before the line

#include <Windows.h> 

because this will cause uncommonly used headers to not be included, which further decreases namespace pollution. See this section of the Microsoft documentation for further information.

Create new file without overwriting existing file

In standard C, the correct way to create a new file without overwriting an existing file is to use fopen (or fopen_s) with the mode "wx" (or "wbx" for binary files). See the documentation for fopen for further information.

If that function fails and fopen sets the value of errno to the Microsoft-specific value EEXIST (File exists), then you can attempt to overwrite the file by calling fopen again without the x in the mode string. If that function call fails with errno set to EACCES (Permission denied), then a likely reason is that it failed due to the file having the "read-only" attribute. You can then call the Windows API function GetFileAttributesA to confirm this. However, if this confirmation is not required, then you probably do not need to #include <Windows.h> at all.

Sign up to request clarification or add additional context in comments.

4 Comments

Using #define WIN32_LEAN_AND_MEAN before #include <Windows.h> does not work when I put the latter in the main header of my project. In looking through the error messages, most of them are caused by collisions with my "typedef const char CCHAR;", "typedef uint64_t ULONG;" and "typedef const uint64_t CULONG;" statements in the header. I use these and others consistently in my project to make the code more readable. The suggestion of putting the function in its own file with <Windows.h> does work, as mentioned earlier.
I also get errors like "warning C4293: '>>': shift count negative or too big, undefined behavior" and "warning C4477: 'printf' : format string '%016llX' requires an argument of type 'unsigned __int64', but variadic argument 1 has type 'ULONG'", so the compiler is confused abut the length of the variable.
@csharp: The printf %llX conversion format specification is for the standard integer type long long, which usually has a size of 64 bits. The standard integer type long on the other hand usually has a size of 32 bits on Windows compilers, but 64 bits on Linux compilers. That is probably why Microsoft defines ULONG also as 32 bits. If you want a data type that is consistently 64 bits on all platforms, then the use of uint64_t (which is provided by standard C) is usually recommended.
@csharp: However, if you put all functions that require #include <Windows.h> into their own .c file, as you appear to have already done successfully, then you don't have to worry about any of this.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.