Overhaul repository structure to add config

I wish I didn't end up doing this all at once, but ah well. Quick rundown:

- CommonCfg contains config/registry functions shared between Grabby and GrbyCfg.
- GrbyCfg provides a dialog for configuring Grabby.

git-svn-id: svn://vcs.sdm.2ki.xyz/Grabby/trunk@16 27729192-006e-004d-b9b5-06fbd0ef7001
This commit is contained in:
snow flurry 2024-02-26 06:18:00 +00:00
parent be95698d10
commit 69476552e8
42 changed files with 2291 additions and 136 deletions

160
CommonCfg/CommonCfg.vcproj Normal file
View file

@ -0,0 +1,160 @@
<?xml version="1.0" encoding="shift_jis"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="CommonCfg"
ProjectGUID="{61829180-05C1-4865-B9C3-FEB983AD6997}"
RootNamespace="CommonCfg"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="4"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="_WIN32_WINNT=0x0400;_WIN32_WINDOWS=0x0400;_DEBUG"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="4"
CompileAs="1"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLibrarianTool"
OutputFile="$(OutDir)/CommonCfg.lib"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="4"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="_WIN32_WINNT=0x0400;_WIN32_WINDOWS=0x0400"
RuntimeLibrary="4"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"
CompileAs="1"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLibrarianTool"
OutputFile="$(OutDir)/CommonCfg.lib"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File
RelativePath=".\Config.c">
</File>
<File
RelativePath=".\MultiString.c">
</File>
<File
RelativePath=".\RegHelper.c">
</File>
<File
RelativePath=".\stdafx.c">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
</File>
<File
RelativePath=".\UploadConfig.c">
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
<File
RelativePath=".\RegHelper.h">
</File>
<File
RelativePath=".\stdafx.h">
</File>
<Filter
Name="Export"
Filter="">
<File
RelativePath=".\Export\Config.h">
</File>
</Filter>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
</Filter>
<File
RelativePath=".\ReadMe.txt">
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

141
CommonCfg/Config.c Normal file
View file

@ -0,0 +1,141 @@
#include "stdafx.h"
const Config DEFAULT_CONFIG = {
{0}, // Use default (My Documents/Screenshots)
FALSE, // Don't upload screenshots
TRUE, // Do keep screenshots
FALSE, // Don't start helper on startup
HSC_WIN10 // Emulate Win7+ "Snipping Tool" shortcut
};
#define OpenRootKey(hkey,access) \
RegCreateKeyEx(HKEY_CURRENT_USER, \
_T(ROOT_KEYNAME), \
0, \
NULL, \
REG_OPTION_NON_VOLATILE, \
access, \
NULL, \
hkey, \
NULL)
// Enumeration callback for GetConfig
static BOOL
ConfigCallback(HKEY hKey,
LPTSTR szValName,
DWORD dwValSize,
DWORD dwValType,
PVOID pData)
{
Config *cfg = pData;
if (!cfg) return FALSE;
CopyIfMatch(hKey, cfg,
ScreenshotDir, REG_PATH_SZ)
else CopyIfMatch(hKey, cfg,
Upload, REG_DWORD)
else CopyIfMatch(hKey, cfg,
KeepScreenshots, REG_DWORD)
else CopyIfMatch(hKey, cfg,
HelperStartup, REG_DWORD)
else CopyIfMatch(hKey, cfg,
HelperShortcut, REG_DWORD)
return TRUE;
}
Config *
GetConfig(void)
{
HKEY hkRootKey = NULL;
BOOL bRes;
DWORD dwErr;
HANDLE hHeap = GetProcessHeap();
Config *cfg = HeapAlloc(hHeap, 0, sizeof(Config));
if (!cfg) return NULL;
// Copy in the default config
CopyMemory(cfg, &DEFAULT_CONFIG, sizeof(Config));
if ((dwErr = OpenRootKey(&hkRootKey, KEY_READ))) {
HeapFree(hHeap, 0, cfg);
SetLastError(dwErr);
return NULL;
}
bRes = RegGetAllValues(hkRootKey, ConfigCallback, cfg);
dwErr = GetLastError();
RegCloseKey(hkRootKey);
if (!bRes) {
FreeConfig(cfg);
SetLastError(dwErr);
return NULL;
}
// If ScreenshotDir is NULL, set it to My Documents
if (!strlen(cfg->ScreenshotDir)) {
HRESULT hrRes;
// afaict, My Pictures isn't an easily accessible folder.
// There's CSIDL_MYPICTURES, but that's XP-only (and also
// points to a virtual folder???).
hrRes = SHGetFolderPath(NULL,
CSIDL_PERSONAL,
NULL,
SHGFP_TYPE_CURRENT,
cfg->ScreenshotDir);
if (FAILED(hrRes)) {
// TODO: error management
// For now, just empty the string
*cfg->ScreenshotDir = 0;
} else {
PathAppend(cfg->ScreenshotDir,
_T("Screenshots"));
}
}
// TODO: Detect startup
return cfg;
}
BOOL
SaveConfig(Config *cfg)
{
HKEY hkRootKey;
DWORD dwErr;
if (!cfg) return FALSE;
if ((dwErr = OpenRootKey(&hkRootKey, KEY_WRITE))) {
SetLastError(dwErr);
return FALSE;
}
StoreConfig(hkRootKey, cfg,
ScreenshotDir, REG_SZ)
StoreConfig(hkRootKey, cfg,
Upload, REG_DWORD)
StoreConfig(hkRootKey, cfg,
KeepScreenshots, REG_DWORD)
StoreConfig(hkRootKey, cfg,
HelperStartup, REG_DWORD)
StoreConfig(hkRootKey, cfg,
HelperShortcut, REG_DWORD)
RegCloseKey(hkRootKey);
return TRUE;
}
void
FreeConfig(Config *cfg)
{
HANDLE hHeap = GetProcessHeap();
if (!cfg) return;
HeapFree(hHeap, 0, cfg);
return;
}

86
CommonCfg/Export/Config.h Normal file
View file

@ -0,0 +1,86 @@
#pragma once
typedef struct _MultiString {
LPTSTR * Values; // Array of string values
size_t Count; // Length of Values
size_t TotalLength; // Used by registry helpers.
// NOTE: This length includes the NULL
// terminator characters of each string
DWORD Reserved[2]; // Used internally
} MultiString;
// Initializes an existing MultiString array, with the given length.
// If dwInitLen is 0, no entries are pre-allocated.
BOOL
MultiString_Init(MultiString *ms,
DWORD dwInitLen);
// Appends a copy of szNew to the end of the list.
//
// NOTE: A new string with the same length as szNew is allocated and
// stored in ms. This makes future internal freeing easier, but means
// the function caller is responsible for freeing the parameter.
BOOL
MultiString_Append(MultiString *ms,
LPCTSTR szNew);
// Removes the string at dwIdx, freeing the memory used.
BOOL
MultiString_Remove(MultiString *ms,
DWORD dwIdx);
// Replaces the string at dwIdx with szNew. Allocations are performed
// similar to MultiString_Append.
BOOL
MultiString_Replace(MultiString *ms,
DWORD dwIdx,
LPCTSTR szNew);
// Frees all memory in a MultiString structure.
void
MultiString_Dispose(MultiString *ms);
typedef enum _HelperShortcut {
HSC_MACOS, // Pretend to be like MacOS (Win+Shift+4)
HSC_WIN10, // Pretend to be like Windows (Win+Shift+S)
HSC_CUSTOM // Use a custom keyboard shortcut
} HlprKbd;
typedef struct _GlobalConfig {
TCHAR ScreenshotDir[MAX_PATH];
BOOL Upload;
BOOL KeepScreenshots;
BOOL HelperStartup;
HlprKbd HelperShortcut;
} Config;
typedef enum _AuthMethod {
CFG_NOAUTH, // Anonymous HTTP requests
CFG_BASICAUTH, // Use HTTP Basic auth
CFG_DIGESTAUTH, // Use HTTP Digest auth
CFG_TOKENAUTH // Use a custom Authorization header
} AuthMethod;
typedef struct _UploadConfig {
LPTSTR Name;
LPTSTR URL;
LPTSTR Method;
/*
* NOTE: If AuthType == CFG_TOKENAUTH, Username is blank/NULL and
* Password is the token.
*/
LPTSTR Username;
LPTSTR Password;
AuthMethod HTTPAuthMethod;
MultiString Headers;
MultiString Parameters;
} UploadConfig;
// Gets the current configuration from the registry.
Config *GetConfig(void);
// Saves the Config object to the registry.
BOOL SaveConfig(Config *cfg);
// Frees all memory associated with the Config object.
void FreeConfig(Config *cfg);

196
CommonCfg/MultiString.c Normal file
View file

@ -0,0 +1,196 @@
#include "stdafx.h"
#define STRSAFE_NO_DEPRECATE
#include <Strsafe.h>
#define CurAlloc Reserved[0]
BOOL
MultiString_Init(MultiString *ms,
DWORD dwInitLen)
{
HANDLE hHeap = GetProcessHeap();
if (!ms) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (dwInitLen) {
ms->CurAlloc = dwInitLen;
ms->Values = HeapAlloc(hHeap,
HEAP_ZERO_MEMORY,
sizeof(LPTSTR) * dwInitLen);
if (!ms->Values) return FALSE;
ms->Count = 0;
} else {
// Otherwise, this amounts to a zero-out
ZeroMemory(ms, sizeof(MultiString));
}
return TRUE;
}
__inline static LPTSTR
CreateStrCopy(LPCTSTR szStr)
{
HRESULT hrRes;
LPTSTR szNewCopy;
// dwLen needs to include the NULL terminator
size_t dwLen = _tcslen(szStr) + 1;
HANDLE hHeap = GetProcessHeap();
// Allocate the new string
szNewCopy = HeapAlloc(hHeap,
HEAP_ZERO_MEMORY,
sizeof(TCHAR) * dwLen);
if (!szNewCopy) return NULL;
// Copy the actual data over
hrRes = StringCchCopy(szNewCopy,
dwLen,
szStr);
if (FAILED(hrRes)) {
HeapFree(hHeap, 0, szNewCopy);
return NULL;
}
return szNewCopy;
}
BOOL
MultiString_Append(MultiString *ms,
LPCTSTR szNew)
{
LPTSTR szNewCopy = NULL;
DWORD dwNewLen;
HANDLE hHeap = GetProcessHeap();
if (!ms || !szNew) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
dwNewLen = (DWORD)_tcslen(szNew) + 1;
// Do we need to allocate more room for pointers?
if (ms->Values == NULL) {
// Oops, we have nothing! Let's allocate a couple
if (!MultiString_Init(ms, 3)) {
return FALSE;
}
} else if (ms->CurAlloc == ms->Count) {
DWORD dwNewAlloc = ms->CurAlloc + 3;
LPTSTR *pszNew = HeapReAlloc(hHeap,
0,
ms->Values,
sizeof(LPTSTR) * dwNewAlloc);
if (!pszNew) {
// ReAlloc failed :<
return FALSE;
}
ms->CurAlloc = dwNewAlloc;
ms->Values = pszNew;
}
szNewCopy = CreateStrCopy(szNew);
if (!szNewCopy) return FALSE;
// All set, append to the end and increment Count
ms->Values[ms->Count++] = szNewCopy;
ms->TotalLength += dwNewLen;
return TRUE;
}
BOOL
MultiString_Remove(MultiString *ms,
DWORD dwIdx)
{
DWORD dwLen = 0;
HANDLE hHeap = GetProcessHeap();
if (!ms) return FALSE;
if (dwIdx >= ms->Count) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
} else if (ms->Values[dwIdx] != NULL) {
dwLen = (DWORD)_tcslen(ms->Values[dwIdx]) + 1;
HeapFree(hHeap, 0, ms->Values[dwIdx]);
}
// Shift down the remaining strings
if (dwIdx < ms->Count - 1) {
size_t i;
for (i = dwIdx; i < ms->Count - 1; i++) {
ms->Values[i] = ms->Values[i + 1];
}
}
ms->Count--;
ms->TotalLength -= dwLen;
return TRUE;
}
BOOL
MultiString_Replace(MultiString *ms,
DWORD dwIdx,
LPCTSTR szNew)
{
DWORD dwOldLen = 0,
dwNewLen = 0;
LPTSTR szTmp;
HANDLE hHeap = GetProcessHeap();
if (!ms || !szNew) return FALSE;
if (dwIdx >= ms->Count) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
dwNewLen = (DWORD)_tcslen(szNew) + 1;
// Create the copied new string
szTmp = CreateStrCopy(szNew);
if (!szTmp) return FALSE;
// Perform the switcheroo
if (ms->Values[dwIdx] != NULL) {
dwOldLen = (DWORD)_tcslen(ms->Values[dwIdx]) + 1;
HeapFree(hHeap, 0, ms->Values[dwIdx]);
}
ms->Values[dwIdx] = szTmp;
ms->TotalLength = ms->TotalLength + dwNewLen - dwOldLen;
return TRUE;
}
void
MultiString_Dispose(MultiString *ms)
{
HANDLE hHeap = GetProcessHeap();
DWORD i;
if (!ms) return;
if (ms->Values != NULL) {
for (i = 0; i < ms->Count; i++) {
HeapFree(hHeap, 0, ms->Values[i]);
}
HeapFree(hHeap, 0, ms->Values);
ms->Values = NULL;
}
ms->Count = 0;
ms->CurAlloc = 0;
return;
}

29
CommonCfg/ReadMe.txt Normal file
View file

@ -0,0 +1,29 @@
========================================================================
STATIC LIBRARY : CommonCfg Project Overview
========================================================================
AppWizard has created this CommonCfg library project for you.
This file contains a summary of what you will find in each of the files that
make up your CommonCfg application.
CommonCfg.vcproj
This is the main project file for VC++ projects generated using an Application Wizard.
It contains information about the version of Visual C++ that generated the file, and
information about the platforms, configurations, and project features selected with the
Application Wizard.
/////////////////////////////////////////////////////////////////////////////
StdAfx.h, StdAfx.cpp
These files are used to build a precompiled header (PCH) file
named CommonCfg.pch and a precompiled types file named StdAfx.obj.
/////////////////////////////////////////////////////////////////////////////
Other notes:
AppWizard uses "TODO:" comments to indicate parts of the source code you
should add to or customize.
/////////////////////////////////////////////////////////////////////////////

280
CommonCfg/RegHelper.c Normal file
View file

@ -0,0 +1,280 @@
#include "stdafx.h"
#define STRSAFE_NO_DEPRECATE
#include <Strsafe.h>
// Used by CopyRegValue specifically for copying
// REG_MULTI_SZ values, because those in particular are
// really funky.
static BOOL
CopyMultiValue(HKEY hKey,
PVOID pVal,
LPCTSTR szName,
DWORD dwSize)
{
int iStrCount = 0;
LPTSTR szMulti = NULL,
ptr;
BOOL bWasNull = FALSE;
LONG lRes;
MultiString *msData = (MultiString *)pVal;
HANDLE hHeap = GetProcessHeap();
szMulti = HeapAlloc(hHeap,
HEAP_ZERO_MEMORY,
dwSize + sizeof(TCHAR));
if (!szMulti) return FALSE;
// Get the multi-string value
if (!(lRes = RegQueryValueEx(hKey,
szName,
0,
NULL,
szMulti,
&dwSize)))
{
HeapFree(hHeap, 0, szMulti);
SetLastError(lRes);
return FALSE;
}
for (ptr = szMulti; (ptr - szMulti) < (LONG)dwSize; ptr++) {
if (*ptr == 0) {
if (bWasNull) {
// Two NULLs in a row, we're done!
break;
} else {
// End of string, add to the list
bWasNull = TRUE;
iStrCount++;
}
}
bWasNull = FALSE;
}
if (!bWasNull) {
// Not properly NULL terminated, sigh...
// Make sure to count the last string.
iStrCount++;
}
if (!MultiString_Init(msData, iStrCount)) {
HeapFree(hHeap, 0, szMulti);
return FALSE;
} else {
int i;
for (i = 0, ptr = szMulti; i < iStrCount; i++, ptr += strlen(ptr) + 1) {
if (!MultiString_Append(msData, ptr)) {
DWORD err = GetLastError();
HeapFree(hHeap, 0, szMulti);
MultiString_Dispose(msData);
SetLastError(err);
return FALSE;
}
}
}
HeapFree(hHeap, 0, szMulti);
return TRUE;
}
BOOL
CopyRegValue(HKEY hKey,
PVOID pVal,
LPCTSTR szName,
DWORD dwType,
DWORD dwSize)
{
HANDLE hHeap = GetProcessHeap();
PVOID pData = pVal;
LONG lRes;
if (pVal == NULL ||
szName == NULL ||
hKey == NULL) {
return FALSE;
}
if (dwType == REG_BINARY ||
dwType == REG_SZ) {
pData = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwSize);
if (!pData) return FALSE;
*(PVOID *)pVal = pData;
} else if (dwType == REG_MULTI_SZ) {
// REG_MULTI_SZ is complex enough to warrant its own
// function
return CopyMultiValue(hKey, pVal, szName, dwSize);
}
if ((lRes = RegQueryValueEx(hKey,
szName,
NULL,
NULL,
pData,
&dwSize)) != ERROR_SUCCESS)
{
SetLastError(lRes);
return FALSE;
}
return TRUE;
}
// Enumerates across all values in a key, running the given
// RegEnumCallback. Use `pData` to pass application-defined
// data to your enumerator callback.
BOOL RegGetAllValues(HKEY hKey,
RegEnumCallback cb,
PVOID pData)
{
LONG lRes;
TCHAR szValName[512];
DWORD dwNameLen = 512,
dwIdx,
dwValType,
dwValSize;
if (!hKey || !cb) return FALSE;
for (dwIdx = 0;
dwNameLen = 512, // reset dwNameLen first
!(lRes = RegEnumValue(hKey,
dwIdx,
szValName,
&dwNameLen,
0,
&dwValType,
NULL,
&dwValSize));
dwIdx++) {
// Enforce null termination
szValName[min(511, dwNameLen)] = 0;
// pass to callback
if (!cb(hKey,
szValName,
dwValSize,
dwValType,
pData)) {
// Callback returned failure, bail out
return FALSE;
}
}
if (lRes != ERROR_NO_MORE_ITEMS) {
// Some enumeration error happened, store it
// and report the failure
SetLastError(lRes);
return FALSE;
}
return TRUE;
}
BOOL
RegStoreMultiString(HKEY hKey,
LPTSTR szValName,
MultiString *msData)
{
LPTSTR szMulti;
DWORD dwMultiSize;
LONG lRes;
HANDLE hHeap = GetProcessHeap();
if (!msData->TotalLength) return FALSE;
// Total size + second NULL terminator
dwMultiSize = (DWORD)msData->TotalLength + 1;
szMulti = HeapAlloc(hHeap,
HEAP_ZERO_MEMORY,
dwMultiSize * sizeof(TCHAR));
if (!szMulti) return FALSE;
// Copy all the strings into one buffer, as per REG_MULTI_SZ
{
DWORD i, dwCurLen;
for (i = 0, dwCurLen = 0;
i < msData->Count && dwCurLen < dwMultiSize;
dwCurLen += (DWORD)_tcslen(msData->Values[i++]) + 1) {
HRESULT hrRes = StringCchCopy(&szMulti[dwCurLen],
_tcslen(msData->Values[i]) + 1,
msData->Values[i]);
if (FAILED(hrRes)) {
HeapFree(hHeap, 0, szMulti);
return FALSE;
}
}
}
lRes = RegSetValueEx(hKey,
szValName,
0,
REG_MULTI_SZ,
szMulti,
dwMultiSize);
HeapFree(hHeap, 0, szMulti);
if (lRes != ERROR_SUCCESS) {
SetLastError(lRes);
return FALSE;
}
return TRUE;
}
// Stores a value in the registry, making assumptions based on the
// key type.
BOOL
RegStoreValue(HKEY hKey,
LPTSTR szValName,
PVOID pData,
DWORD dwValType)
{
DWORD dwStoreSize;
LONG lRes;
if (!hKey || !szValName || !pData) return FALSE;
switch (dwValType) {
case REG_SZ:
case REG_EXPAND_SZ:
dwStoreSize = (DWORD)_tcslen((LPTSTR)pData) + sizeof(TCHAR);
break;
case REG_DWORD:
dwStoreSize = sizeof(DWORD);
break;
#ifdef QWORD
case REG_QWORD:
dwStoreSize = sizeof(QWORD);
break;
#endif
case REG_BINARY:
// TODO: handle REG_BINARY? will we ever use it?
OutputDebugString(_T("REG_BINARY attempted to be stored! Unsupported"));
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
case REG_MULTI_SZ:
return RegStoreMultiString(hKey,
szValName,
(MultiString *)pData);
default:
// ignore
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
lRes = RegSetValueEx(hKey,
szValName,
0,
dwValType,
pData,
dwStoreSize);
if (lRes != ERROR_SUCCESS) {
SetLastError(lRes);
return FALSE;
}
return TRUE;
}

68
CommonCfg/RegHelper.h Normal file
View file

@ -0,0 +1,68 @@
// Helper functions for handling the registry
#pragma once
// Used behind CopyIfMatch. Copies a given registry value to
// another location, regardless of type.
BOOL CopyRegValue(HKEY hKey,
PVOID pVal,
LPCTSTR szName,
DWORD dwType,
DWORD dwSize);
// Callback for RegGetAllValues
typedef BOOL (*RegEnumCallback)(HKEY hKey,
LPTSTR szValName,
DWORD dwValSize,
DWORD dwValType,
PVOID pData);
// Enumerates across all values in a key, running the given
// RegEnumCallback. Use `pData` to pass application-defined
// data to your enumerator callback.
BOOL RegGetAllValues(HKEY hKey,
RegEnumCallback cb,
PVOID pData);
// Wrapper function for storing reg values, regardless of type
BOOL RegStoreValue(HKEY hKey,
LPTSTR szValName,
PVOID pData,
DWORD dwValType);
// Used primarily by RegEnumCallback, hence the hardcoded
// var names.
// You might want to check out one of the Get*Config functions
// for more background on how to use this, but it should make the
// callback function as easy as:
//
// CopyIfMatch(hKey, myStruct,
// myField, REG_SZ) // or any type
// else CopyIfMatch(hKey, myStruct,
// myOtherField, REG_DWORD) // also note: no semicolons
// // ... and so on
#define CopyIfMatch(hk,x,name,type) \
if (!_tcsncmp(_T(#name), \
szValName, \
_tcslen(_T(#name))) \
&& (dwValType & 0xfff) == type) { \
if (!CopyRegValue(hk, \
&((x)->name), \
_T(#name), \
type, \
dwValSize)) { \
return FALSE; \
} \
}
// Helper macro for Save*Config functions. Assumes a BOOL-returning
// function in a vacuum.
#define StoreConfig(hk,x,name,type) \
if (!RegStoreValue(hk, _T(#name), &(x)->name, type)) { \
DWORD dwRegErr = GetLastError(); \
RegCloseKey(hk); \
return FALSE; \
}
#define ROOT_KEYNAME "SOFTWARE\\Grabby"
// Hack to handle REG_SZ values that are actually TCHAR[MAX_PATH].
#define REG_PATH_SZ (0xf000 & REG_SZ)

65
CommonCfg/UploadConfig.c Normal file
View file

@ -0,0 +1,65 @@
#include "stdafx.h"
static BOOL
UploadConfigCb(HKEY hKey,
LPTSTR szValName,
DWORD dwValSize,
DWORD dwValType,
PVOID pData)
{
UploadConfig *ucfg = pData;
if (!ucfg) return FALSE;
CopyIfMatch(hKey, ucfg,
Name, REG_SZ)
else CopyIfMatch(hKey, ucfg,
URL, REG_SZ)
else CopyIfMatch(hKey, ucfg,
Method, REG_SZ)
else CopyIfMatch(hKey, ucfg,
Username, REG_SZ)
else CopyIfMatch(hKey, ucfg,
Password, REG_SZ)
else CopyIfMatch(hKey, ucfg,
HTTPAuthMethod, REG_DWORD)
else CopyIfMatch(hKey, ucfg,
Headers, REG_MULTI_SZ)
else CopyIfMatch(hKey, ucfg,
Parameters, REG_MULTI_SZ)
return TRUE;
}
UploadConfig *
GetUploadConfig(void)
{
HKEY hkUploadKey = NULL;
HANDLE hHeap = GetProcessHeap();
LONG lRes;
UploadConfig *ucfg = HeapAlloc(hHeap,
HEAP_ZERO_MEMORY,
sizeof(UploadConfig));
if (!ucfg) return NULL;
// TODO: defaults?
lRes = RegOpenKeyEx(HKEY_CURRENT_USER,
_T(ROOT_KEYNAME "\\Upload"),
0,
KEY_READ,
&hkUploadKey);
if (lRes) {
SetLastError(lRes);
HeapFree(hHeap, 0, ucfg);
return NULL;
}
if (!RegGetAllValues(hkUploadKey, UploadConfigCb, ucfg)) {
HeapFree(hHeap, 0, ucfg);
return NULL;
}
return ucfg;
}

1
CommonCfg/stdafx.c Normal file
View file

@ -0,0 +1 @@
#include "stdafx.h"

17
CommonCfg/stdafx.h Normal file
View file

@ -0,0 +1,17 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <Windows.h>
#include <tchar.h>
#include <ShlObj.h>
#include <shlwapi.h>
// TODO: reference additional headers your program requires here
#include "RegHelper.h"
#include "Export/Config.h"

View file

@ -1,5 +1,13 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Grabby", "Grabby.vcproj", "{700E5DD7-DD00-46D9-92DF-2C132940F53D}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Grabby", "Grabby\Grabby.vcproj", "{700E5DD7-DD00-46D9-92DF-2C132940F53D}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GrbyCfg", "GrbyCfg\GrbyCfg.vcproj", "{BBB3E933-71CC-4EF7-BF56-52371B9AF938}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonCfg", "CommonCfg\CommonCfg.vcproj", "{61829180-05C1-4865-B9C3-FEB983AD6997}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
@ -13,6 +21,14 @@ Global
{700E5DD7-DD00-46D9-92DF-2C132940F53D}.Debug.Build.0 = Debug|Win32
{700E5DD7-DD00-46D9-92DF-2C132940F53D}.Release.ActiveCfg = Release|Win32
{700E5DD7-DD00-46D9-92DF-2C132940F53D}.Release.Build.0 = Release|Win32
{BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Debug.ActiveCfg = Debug|Win32
{BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Debug.Build.0 = Debug|Win32
{BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Release.ActiveCfg = Release|Win32
{BBB3E933-71CC-4EF7-BF56-52371B9AF938}.Release.Build.0 = Release|Win32
{61829180-05C1-4865-B9C3-FEB983AD6997}.Debug.ActiveCfg = Debug|Win32
{61829180-05C1-4865-B9C3-FEB983AD6997}.Debug.Build.0 = Debug|Win32
{61829180-05C1-4865-B9C3-FEB983AD6997}.Release.ActiveCfg = Release|Win32
{61829180-05C1-4865-B9C3-FEB983AD6997}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection

View file

@ -5,6 +5,7 @@
#include "Bitmap.h"
#include "Overlay.h"
#include "Grabby.h"
#include <Config.h>
/**
* Print an error with a given context before bailing out
@ -43,7 +44,7 @@ void Die(LPTSTR lpContextMsg)
_T("Catastrophic Error"),
MB_OK | MB_ICONERROR);
} else {
_sntprintf(lpszFullMsg, iFullLen, lpszFmt, lpContextMsg, lpszError);
StringCchPrintf(lpszFullMsg, iFullLen, lpszFmt, lpContextMsg, lpszError);
MessageBox(NULL,
lpszFullMsg,
_T("Fatal Error"),
@ -62,16 +63,24 @@ int APIENTRY _tWinMain(HINSTANCE hInstance,
SYSTEMTIME stNow = {0};
RECT capRegion = {0};
Screen *scrn = CreateScreen();
#define OUTPUT_DIR_LEN 96
TCHAR szOutputDir[OUTPUT_DIR_LEN + 1];
#define FULL_OUT_PATH_LEN OUTPUT_DIR_LEN + 24
TCHAR szFullOutPath[FULL_OUT_PATH_LEN + 1];
Config *cfg;
TCHAR szFullOutPath[MAX_PATH];
HRESULT hRes;
if (!scrn) {
Die("Couldn't get screen information");
}
cfg = GetConfig();
if (!cfg) {
Die("Couldn't get config");
}
if (!PathFileExists(cfg->ScreenshotDir) &&
!CreateDirectory(cfg->ScreenshotDir, NULL)) {
Die("Couldn't create configured folder");
}
switch (CreateOverlay(hInstance, scrn)) {
case -1:
Die("Couldn't create overlay window");
@ -86,16 +95,12 @@ int APIENTRY _tWinMain(HINSTANCE hInstance,
GetChosenRect(&capRegion);
if (!GetTempPath(OUTPUT_DIR_LEN, szOutputDir)) {
Die("Couldn't get temporary directory");
}
GetLocalTime(&stNow);
hRes = StringCchPrintf(szFullOutPath,
FULL_OUT_PATH_LEN,
"%s\\scrn_%04d%02d%02d_%02d%02d%02d.bmp",
szOutputDir,
MAX_PATH,
"%s\\Screenshot at %04d-%02d-%02d %02d.%02d.%02d.bmp",
cfg->ScreenshotDir,
stNow.wYear,
stNow.wMonth,
stNow.wDay,

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -1,4 +1,4 @@
//Microsoft Visual C++ generated resource script.
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
@ -10,12 +10,58 @@
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Japanese resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
LANGUAGE 17, 1
#ifdef _WIN32
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
#pragma code_page(932)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
"#include ""windows.h""\r\n"
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // Japanese resources
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
@ -24,95 +70,10 @@ LANGUAGE 17, 1
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_GRABBY ICON "Grabby.ico"
IDI_SMALL ICON "small.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDC_GRABBY MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", IDM_EXIT
END
POPUP "&Help"
BEGIN
MENUITEM "&About ...", IDM_ABOUT
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//
IDC_GRABBY ACCELERATORS
BEGIN
"?", IDM_ABOUT, ASCII, ALT
"/", IDM_ABOUT, ASCII, ALT
END
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_ABOUTBOX DIALOG 22, 17, 230, 75
STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "About"
FONT 9, "System"
BEGIN
ICON IDI_GRABBY,IDC_MYICON,14,9,16,16
LTEXT "Grabby Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX
LTEXT "Copyright (C) 2024",IDC_STATIC,49,20,119,8
DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
END
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
"#include ""windows.h""\r\n"
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDC_GRABBY "GRABBY"
IDS_APP_TITLE "Grabby"
END
#endif
IDI_GRABBY ICON "Grabby.ico"
IDI_SMALL ICON "small.ico"
IDI_ICON1 ICON "icon1.ico"
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
@ -126,3 +87,4 @@ END
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View file

@ -19,6 +19,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="D:\Projects\curl\include;..\CommonCfg\Export"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
@ -32,8 +33,10 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="libcurl_a.lib shlwapi.lib"
OutputFile="$(OutDir)/Grabby.exe"
LinkIncremental="2"
AdditionalLibraryDirectories="D:\Projects\curl\lib"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/Grabby.pdb"
SubSystem="2"
@ -67,18 +70,22 @@
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="D:\Projects\curl\include;..\CommonCfg\Export"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
RuntimeLibrary="4"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
DebugInformationFormat="3"
CompileAs="1"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="libcurl_a.lib shlwapi.lib"
OutputFile="$(OutDir)/Grabby.exe"
LinkIncremental="1"
AdditionalLibraryDirectories="D:\Projects\curl\lib"
GenerateDebugInformation="TRUE"
SubSystem="2"
OptimizeReferences="2"
@ -107,6 +114,9 @@
</Configuration>
</Configurations>
<References>
<ProjectReference
ReferencedProjectIdentifier="{61829180-05C1-4865-B9C3-FEB983AD6997}"
Name="CommonCfg"/>
</References>
<Files>
<Filter
@ -159,12 +169,15 @@
<File
RelativePath=".\Grabby.rc">
</File>
<File
RelativePath=".\icon1.ico">
</File>
<File
RelativePath=".\small.ico">
</File>
</Filter>
<File
RelativePath=".\ReadMe.txt">
RelativePath="..\ReadMe.txt">
</File>
</Files>
<Globals>

BIN
Grabby/icon1.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

36
Grabby/resource.h Normal file
View file

@ -0,0 +1,36 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Grabby.rc
//
#define IDC_MYICON 2
#define IDD_GRABBY_DIALOG 102
#define IDS_APP_TITLE 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_GRABBY 107
#define IDI_SMALL 108
#define IDC_GRABBY 109
#define IDR_MAINFRAME 128
#define IDI_ICON1 129
#define IDC_TABVIEW 1000
#define IDC_APPLY 1001
#define IDC_EDIT1 1004
#define IDC_BUTTON1 1005
#define IDC_CHECK1 1006
#define IDC_URLINPUT 1007
#define IDC_ENCODEFILE 1008
#define IDC_COMBO1 1009
#define IDC_UPLOADMETHOD 1009
#define IDC_STATIC -1
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 132
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1010
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -9,6 +9,7 @@
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <shlwapi.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
@ -16,4 +17,4 @@
#include <stdio.h>
#include <tchar.h>
#include <Strsafe.h>
#include <Strsafe.h>

11
GrbyCfg/Dialog.c Normal file
View file

@ -0,0 +1,11 @@
#define _IS_DIALOG_C_
#include "stdafx.h"
#include "Dialog.h"
void
InitUserMsgs(void)
{
DEFINE_USER_MSG(WMU_SAVE_CONFIG);
DEFINE_USER_MSG(WMU_SETTING_CHANGED);
}

25
GrbyCfg/Dialog.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
#define _DLG_MSG_SUFFIX "{990f4b34-579a-4fbc-a5b2-08f46ef5e9c5}"
#define USER_MSG_NAME(name) _T( #name _DLG_MSG_SUFFIX )
#define DEFINE_USER_MSG(name) {\
LPCTSTR name##_MSG = USER_MSG_NAME(name); \
name = RegisterWindowMessage(name##_MSG); \
}
#define DECLARE_USER_MSG(name) \
UINT name;
/* --- */
DECLARE_USER_MSG(WMU_SAVE_CONFIG)
DECLARE_USER_MSG(WMU_SETTING_CHANGED)
void InitUserMsgs(void);
#define ChangedSetting(hwnd) \
SendMessage(hwnd, \
WMU_SETTING_CHANGED, \
0, 0)
#define IsChecked(ref) \
(IsDlgButtonChecked(hDlg, ref) == BST_CHECKED)

4
GrbyCfg/General.h Normal file
View file

@ -0,0 +1,4 @@
#pragma once
// Define the dialog proc
DEF_DLGPROC(GeneralTab);

182
GrbyCfg/GeneralCfg.c Normal file
View file

@ -0,0 +1,182 @@
/* General configuration dialog */
#include "stdafx.h"
#include "GrbyCfg.h"
int CALLBACK
ScreenshotBrowseCallback(HWND hWnd,
UINT uMsg,
LPARAM lParam,
LPARAM lpData)
{
if (uMsg == BFFM_INITIALIZED && lpData) {
// Set root dir
SendMessage(hWnd,
BFFM_SETSELECTION,
1,