How determine application subsystem from executable file - c#

I'm trying to detect console application from the list of the executables files installed on my computer.
How to implement it?
Every application has a "subsystem" (windows application, console application or library; specified to the linker as option, I think). How to detect it using only the executable file?
Are there alternative methods to detect the application characteristic? Additionally, are there any method for detecting the file is a really executable file?
Any issue for JAR executables?

Without any programming you receive this information from
dumpbin.exe /headers filename
Some information gives you GetBinaryType and SHGetFileInfo functions. All information which you needs you will find in the header of every executable file. See Microsoft Portable Executable and Common Object File Format Specification in http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx.
One can use also Debug Help Library API from DbgHelp.dll (see http://msdn.microsoft.com/en-us/library/ms679309(VS.85).aspx). IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE and IMAGE_NT_HEADERS32 structures gives you full information.
UPDATED (add some code):
Or you can use only structures defined in WinNT.h. The corresponding code can start like following
// Open source file
hSrcFile = CreateFile (pszSrcFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hSrcFile == INVALID_HANDLE_VALUE)
__leave;
// Map the source file in memory
hMapSrcFile = CreateFileMapping (hSrcFile, NULL, PAGE_READONLY, 0, 0, NULL); // SEC_IMAGE
if (!hMapSrcFile || hMapSrcFile == INVALID_HANDLE_VALUE)
__leave;
// Map the entire of the source file is memory
pSrcFile = (PBYTE) MapViewOfFile (hMapSrcFile, FILE_MAP_READ, 0, 0, 0);
if (!pSrcFile)
__leave;
pDosHeader = (IMAGE_DOS_HEADER *)pSrcFile;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
printf ("it is not a EXE file.\n");
return 1;
}
printf ("IMAGE_DOS_HEADER size %d (0x%X) bytes\n", sizeof(IMAGE_DOS_HEADER), sizeof(IMAGE_DOS_HEADER));
DumpDosHeader (pDosHeader);
pDosExeStart = (PBYTE)pDosHeader + pDosHeader->e_cparhdr*16;
if (g_bDump)
HexDump (1, pDosExeStart, pDosHeader->e_lfanew - pDosHeader->e_cparhdr*16, (DWORD)pDosExeStart);
if (pDosHeader->e_lfanew) {
IMAGE_NT_HEADERS32 *pNtHeader = (IMAGE_NT_HEADERS32 *)((PBYTE)pDosHeader + pDosHeader->e_lfanew);
//IMAGE_NT_HEADERS64 *pNtHeader64 = (IMAGE_NT_HEADERS64 *)((PBYTE)pDosHeader + pDosHeader->e_lfanew);
IMAGE_SECTION_HEADER *pFirstSectionHeader = (IMAGE_SECTION_HEADER *)((PBYTE)&pNtHeader->OptionalHeader +
pNtHeader->FileHeader.SizeOfOptionalHeader);
if (pNtHeader->Signature == IMAGE_NT_SIGNATURE) {
int i;
printf ("\nPE signature\n");
printf ("\nIMAGE_FILE_HEADER: size %d (0x%X) bytes, offset from the begin of the file: %d (0x%X)\n",
sizeof(IMAGE_FILE_HEADER), sizeof(IMAGE_FILE_HEADER),
((PBYTE)&pNtHeader->FileHeader - (PBYTE)pDosHeader), ((PBYTE)&pNtHeader->FileHeader - (PBYTE)pDosHeader));
DumpFileHeader (1, &pNtHeader->FileHeader);
switch (pNtHeader->OptionalHeader.Magic) {
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
printf ("\nIMAGE_OPTIONAL_HEADER32: size %d (0x%X) bytes, offset from the begin of the file: %d (0x%X)\n",
sizeof(IMAGE_OPTIONAL_HEADER32), sizeof(IMAGE_OPTIONAL_HEADER32),
((PBYTE)&pNtHeader->OptionalHeader - (PBYTE)pDosHeader), ((PBYTE)&pNtHeader->OptionalHeader - (PBYTE)pDosHeader));
DumpOptionalHeader32 (1, &pNtHeader->OptionalHeader);
break;
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
break;
case IMAGE_ROM_OPTIONAL_HDR_MAGIC:
break;
}

To determine the subsystem you need to read the executable file and parse the PE-Header. A detailed article on how to do that is found here.
A JAR file is just a ZIP file with some specific files and folder structure so you can open it like a regular zip file and look for those files and folders that are always there.

Windows PE executables have a field in the header that specify the subsystem (Console, GUI, Posix, and so on). They also have fields that can be used to identify executables in general. Download the PE specification from msdn.com to get the details.

It's possible to detect subsystem type using PeNet library, for example change see:
using PeNet;
...
var file = new PeFile(<path>);
if (file.ImageNtHeaders.OptionalHeader.Subsystem == 2 /*PeNet.Header.Pe.SubsystemType.WindowsGui*/)
{
// This is UI executable...
}
See also similar commit.
Use 0.5.0 version to get less dependencies, or use latest to get latest version.

Related

Changing the current working directory of cmd (from a child process)

So I am trying to write a cd -like program that can be executed using cmd and after it exits the working directory of the calling cmd process should be changed.
Now before this post is flagged as a duplicate: I am aware of this and this question that were asked for pretty much this exact problem but using Linux instead of Windows as well as being pretty broad and unspecific, and I am aware that similar limitations apply to Windows as well (changing the working directory of my process will not change the parent’s working directory).
There is actually is a working solution to this for linux. However it is using gdb for this, and I would like to achieve this task using only built-in Windows utilities (WinAPI, dotNET, etc.).
What I have tried so far
I did manage to use Cheat Engine and the OpenProcess() / WriteProcessMemory() WinAPI funtions to successfully override cmd's working directory. However this solution feels sloppy and doesn't work well (or at least requires more work to be put into.)
My question
Is there a different (maybe simpler?) way on Windows to achieve this? Like a way to invoke/inject code to the cmd process to execute cd whatever\directory\I\want directly without overriding its memory? I have seen the CreateRemoteThread() functions however I didn't manage to find a way to put them to use.
FYI: I am mainly using C# but C/C++ solutions should help too as long as they are based on the native Microsoft libraries.
This post describes a Windows implementation of a function that launches a child process, creates pipes to stdin and stdout from which a command is sent, and a response is returned. Finally, once all response is captured the child process is terminated. If this sounds familiar it is similar in concept to Linux's popen() function with the exception that this implementation was specifically created to capture the response into a buffer of any command that returns one. (Also included is a variant for use when no-response is expected or needed.)
The full source can be adapted for use within a standalone executable, or as an API. (.dll) Either way, the resulting functions accept and process any command using standard Windows CMD syntax. The function cmd_rsp(...) returns the Windows response via stdout into a self-sizing buffer.
The exported prototypes are:
int __declspec(dllexport) cmd_rsp(const char *command, char **chunk, unsigned int size);
int __declspec(dllexport) cmd_no_rsp(const char *command);
A simple use case when capturing a response:
#include "cmd_rsp.h"
int main(void)
{
char *buf = {0};
buf = calloc(100, 1);//initialize to some initial size
if(!buf)return 0;
cmd_rsp("dir /s", &buf, 100);//buffer will grow to accommodate response as needed.
printf("%s", buf);
free(buf);
return 0;
}
A simple use case when response is not needed:
#include "cmd_rsp.h"
int main(void)
{
cmd_no_rsp("cd C:\\dir1\\dir2");
return 0;
}
A detailed description of purpose and usage is described in the link provided above. To illustrate, here are a few sample command inputs, each in this case change the working directory, then execute a command from that directory:
A command to change to sqlite directory, then execute a query:
cd c:\\tempExtract\\sqlite\\Tools\\sqlite-tools-win32-x86-3250300 && sqlite3.exe .\\extract.db \"select * from event, eventdata where eventType=38 and eventdata .eventid=event.eventid\
A command to change to teraterm directory, then execute a script:
"c:\\Program Files (x86)\\teraterm\" && ttpmacro c:\\DevPhys\\LPCR_2\\play\\Play.ttl
A command to change directory then execute a command to send multiple digital acquisition channel settings.
cd C:\\Dir1\\Dir2\\Dir3\\support\\Exes\\WriteDigChannel && .\\WriteDigChannel.exe P1_CH0 1 && .\\WriteDigChannel.exe P1_C H0 0 && .\\WriteDigChannel.exe P1_CH0 1
A recursive directory search from a specified location:
cd C:\\dir1\\dir2 && dir /s /b
I got it working. As was suggested SendInput finally did the trick.
I used a combination of WinAPI calls to GetForegroundWindow() / SetForegroundWindow() and the Windows Forms System.Windows.Forms.SendKeys.SendWait() Method to achieve what I wanted:
Upon calling my cd-wrapper program (sd.exe) and providing my custom target directory (~/ home) it generates the corresponding command along with the "Enter-Pressed-Event" to be sent to it's parent cmd process.
Here's the complete C# code:
if (args.Length != 1)
{
Console.WriteLine(Directory.GetCurrentDirectory());
return;
}
string targetDirectory = args[0];
string command = string.Empty;
if (targetDirectory.Equals("~"))
{
command = #"pushd C:\Users\fred\Desktop";
}
else if (!Directory.Exists(targetDirectory))
{
Console.WriteLine("I/O Error: No such file or directory.");
return;
}
else
{
command = #"cd " + targetDirectory;
}
Target target = Target.Create(Process.GetCurrentProcess().GetParentProcess());
target.SendKeys(command + "{ENTER}", true);
Note that I kind of started to write a complete Framework for this and similar problems alongside this project that contains all my different approaches to this question and the low level WinAPI calls as well as the Extension methods to get the parent process :D
As it would be a bit overkill to paste all of it's code in this answer, here's the GitHub. If I can find the time I'll go ahead and optimize the code, but for now this'll do. Hope this helps anyone encountering a similar problem :)
Edit:
An even "cleaner" way is to use dll injection to directly make cmd switch it's working directory. While it is a lot harder to get working it has the advantage of not littering the cmd command history as compared to the approach described above. In addition to that cmd seems to be aware of any changes to it's current working directory, so it automatically updates the prompt text. Once I have a fully working example, that allows to dynamically specify the target directory I will post it here :)

How to get hdd transfer mode?

I need to get hdd transfer mode(dma or pio) and print it, you can find it in device manager(in red circle in the screenshot).
So I need to get the info in red circle from programm. I've tried to use wmi classes, but Win32_DiskDrive, Win32_IDEController and others dont't provide the information I need. The closest to the property window from device manager was Win32_IDEController, field Win32_IDEController["Name"] returns string ATA Channel 1.
Also I've found this https://msdn.microsoft.com/en-us/library/windows/hardware/ff550142(v=vs.85).aspx , but it use irb.h, that is part of ddk(wdk) and I've never used it before, so I don't even know how to use this function.
Now I'm learning the WDK) Any solution in any language will good, in project I'm using C#, but if the solution will be in another language I can write "DMA" or "PIO" to a file in this solution, execute it .exe from C# and read from file. OFC solution in C# will be appreciated more.
You can use autoit (https://www.autoitscript.com) to read it direct from the GUI.
Sample (be carefull with different Windows versions and different languages):
Run ("mmc c:\windows\system32\devmgmt.msc")
WinWaitActive ( "Device Manager" )
send("{tab}{down}{down}{down}{down}{down}{down}{down}{NUMPADADD}{down}!{enter}")
WinWaitActive ( "Primary IDE Channel Properties" )
send("^{tab}")
$drivemode = ControlGetText("Primary IDE Channel Properties", "", "Static4")
ControlClick("Primary IDE Channel Properties","Cancel","Button6")
WinKill ( "Device Manager" )
If you want to use Autoit in C#:
https://www.autoitscript.com/forum/topic/177167-using-autoitx-from-c-net/
You can use the AdapterUsesPio member from the STORAGE_ADAPTER_DESCRIPTOR structure. Here is a C++ example that demonstrates how to query a disk for it:
#include "stdafx.h"
int main()
{
wchar_t path[1024];
wsprintf(path, L"\\\\?\\C:"); // or L"\\\\.\\PhysicalDrive0"
// note we use 0, not GENERIC_READ to avoid the need for admin rights
// 0 is ok if you only need to query for characteristics
HANDLE device = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
if (device == INVALID_HANDLE_VALUE)
return 0;
STORAGE_PROPERTY_QUERY query = {};
query.PropertyId = StorageAdapterProperty;
query.QueryType = PropertyStandardQuery;
STORAGE_ADAPTER_DESCRIPTOR descriptor = {};
DWORD read;
if (!DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY,
&query,
sizeof(query),
&descriptor,
sizeof(descriptor),
&read,
NULL
))
{
wprintf(L"DeviceIoControl error: %i\n", GetLastError());
}
else
{
wprintf(L"AdapterUsesPio: %i\n", descriptor.AdapterUsesPio);
}
CloseHandle(device);
return 0;
}

Cannot create DICOMDIR file using GDCM library

I am using GDCM library to create a DICOMDIR file. I implemented the code as shown in GDCM docs:
http://gdcm.sourceforge.net/html/GenerateDICOMDIR_8cs-example.html
In the code:
private int GenerateDicomDir(string directory, string outFileName)
{
gdcm.Directory d = new gdcm.Directory();
uint nfiles = d.Load(directory, true);
if (nfiles == 0) return 1;
string descriptor = "Descriptor";
FilenamesType filenames = d.GetFilenames();
gdcm.Trace.DebugOn();
gdcm.DICOMDIRGenerator gen = new DICOMDIRGenerator();
gen.SetFilenames(filenames);
gen.SetDescriptor(descriptor);
if (!gen.Generate())
{
return 1;
}
gdcm.FileMetaInformation.SetSourceApplicationEntityTitle("GenerateDICOMDIR");
gdcm.Writer writer = new Writer();
writer.SetFile(gen.GetFile());
writer.SetFileName(outFileName);
if (!writer.Write())
{
return 1;
}
return 0;
}
The function returns and does not generate a DICOMDIR file. I have added trace debug on but still cannot debug or get any output message.
Is there any way to generate DICOMDIR file for bunch of DICOM files ?
As per the documentation, did you made sure that:
Warning: : PS 3.11 - 2008 / D.3.1 SOP Classes and Transfer Syntaxes
Composite Image & Stand-alone Storage are required to be stored as
Explicit VR Little Endian Uncompressed (1.2.840.10008.1.2.1). When a
DICOM file is found using another Transfer Syntax the generator will
simply stops. Input files should be Explicit VR Little Endian
filenames should be valid VR::CS value (16 bytes, upper case ...)
If you turn verbose debugging you could log the exact error message, see gdcm::Trace for usage.
As per the documentation of gdcm::Trace, you need to pay attention to the following:
Warning: All string messages are removed during compilation time when
compiled with CMAKE_BUILD_TYPE being set to either: Release MinSizeRel
It is recommended to compile with RelWithDebInfo and/or Debug during
prototyping of applications.
You could also use gdcm::Trace::SetStreamToFile, to properly redirect any messages to a file (instead of stdout by default).
Since you use the recursion option of gdcm.Directory, you need to also pay attention that sub-directory name are valid (VR::CS, 16bytes, upper case...).
See also the gdcmgendir man page for more info.

Cannot access Sqlite Database (of Firefox) using System.Data.Sqlite

I wrote a small C#/.Net application that is able to read the cookies.sqlite file of Firefox. Since I upgraded to Firefox 4 my application is not able to open the database file:
Executing the line "connection.Open();" (in the code sample below) there will be an execption that says:
"File opened that is not a database file. file is encrypted or is not a database"
This is my program code:
class Program
{
static void Main()
{
const string PATH_TO_DATABASE = #"C:\Users\Boris\Desktop\TEMP\cookies.sqlite";
const string CONNECTION_STRING = #"Data Source=" + PATH_TO_DATABASE;
if (!File.Exists(PATH_TO_DATABASE)) return;
using (SQLiteConnection connection = new SQLiteConnection(CONNECTION_STRING))
{
connection.Open();
using (SQLiteCommand command = new SQLiteCommand("SELECT id, name, host, path FROM moz_cookies", connection))
{
using (SQLiteDataReader read = command.ExecuteReader())
{
while (read.Read())
{
string id = read[0].ToString();
string name = read[1].ToString();
string host = read[2].ToString();
string path = read[3].ToString();
Console.WriteLine("ID: " + id);
Console.WriteLine("Name: " + name);
Console.WriteLine("Host: " + host);
Console.WriteLine("Path: " + path);
}
}
}
}
}
}
I am using the .Net Wrapper DLL for Sqlite v. 3.6.23.1.
The target framework of the application is .Net 2.0.
I was able to open the sqlite database without any problems using an application called SqliteExpert.
It would be great if anybody has an idea!
Regards,
Boris
Firefox 4.0 uses SQLite version 3.7.4. (To see this, download SQLite Manager and run select sqlite_version(); in the "Execute SQL" tab.)
It appears that v3.7.4 creates databases that can't be read by v3.6.23.1. I can't find this explicitly stated in the release notes, but it's clear from comparing the cookies.sqlite file to the file format documentation that the format has changed. Specifically, according to the documentation, byte 19 (0x13) should be 1, but that byte in the cookies.sqlite file is 2. As per the documentation:
If a value greater than 1 is read by
SQLite, then the library will refuse
to open the database.
Like the "write version" described
above, this field exists to facilitate
some degree of forwards compatibility,
in case it is ever required. If a
version of SQLite created in the
future uses a file format that may not
be safely read by older SQLite
versions, then this field will be set
to a value greater than 1.
To read the database, you will need to use the latest version of SQLite; unfortunately System.Data.SQLite hasn't been updated in almost a year. The project has since been taken over by sqlite.org, but downloads are not currently available.
If you don't want to wait for sqlite.org to release a v3.7.x-based .NET wrapper, you could try downloading the source and manually upgrading SQLite.NET/SQLite.Interop/src/sqlite3.c to the latest amalgamation C source file (available here).
EDIT: As noted by sdwilsh, Firefox 4.0 uses Write-Ahead Logging; as the description of that new journal mode states:
Thus, if an older version of SQLite
attempts to connect to an SQLite
database that is operating in WAL
mode, it will report an error along
the lines of "file is encrypted or is
not a database".
I had the same problem. Previously I used a Python script (with sqlite integration) to fetch up data from the cookies.sqlite and perms.sqlite files. The latter still works fine. Indeed, all of the other .sqlite files are readable ... except for cookies.sqlite and places.sqlite. Those two produce the "encrypted or is not a database" error.
Rather than hunt down an upgrade for Python (or sqlite3), I created a patch for the .sqlite files. This C-code changes the bytes at offset 18 and 19 from 2 to 1. I run this on Windows under Cygwin, but it should compile and run on Unix/Linux.
Warning: Don't do this to the original Firefox cookies.sqlite file.
Instead, copy that to a temp file, then run the patch on the copy.
// ffpatch.c
// Edits the specified Firefox .sqlite file.
// Changes 0x0202 to 0x0101 at offset 18/19.
// BEFORE
// 0000000 S Q L i t e f o r m a t 3 \0
// 0000020 004 \0 002 002 \0 # \0 \0 005 034 \0 \0 \0 N
// AFTER
// 0000000 S Q L i t e f o r m a t 3 \0
// 0000020 004 \0 001 001 \0 # \0 \0 005 034 \0 \0 \0 N
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#define PROGNAME "ffpatch"
#define MY_OFFSET 18
int main (int argc, char *argv[])
{
int fd;
char buf[2];
off_t offset;
ssize_t wsize;
if (argc != 2)
{
fprintf(stderr, "usage: %s sqlite-file\n", PROGNAME);
exit(1);
}
if ((fd = open(argv[1], O_RDWR)) == -1)
{
fprintf(stderr, "cannot open %s\n", argv[1]);
exit(1);
}
if ((offset = lseek(fd, MY_OFFSET, SEEK_SET)) != MY_OFFSET)
{
fprintf(stderr, "lseek() failed\n");
exit(1);
}
buf[0] = 1;
buf[1] = 1;
wsize = write(fd, buf, 2);
close(fd);
exit(0);
}
Hello and thanks alot for your answers!
I made my application work this way:
1.
I downloaded the sources of the current ADO.NET Connector for .NET 4.0 here: http://system.data.sqlite.org/index.html/timeline?r=trunk
Maybe you will have to logon on the website first using a anonymous user name and a capcha as passoword.
2.
I obtained the precompiled version of the sqlite3.dll v.3.7.4 by downloading an installing this ADO.NET connector: http://www.devart.com/dotconnect/sqlite/
(You may use the ADO.NET connector libs as an replacement for connector from system.data.sqlite.org as well.
I myself am only interested in the sqlite3.dll.)
3.
After compiling the sources from system.data.sqlite.org I copied the resulting System.Data.Sqlite.dll and the sqlite3.dll to my applications output directory. Please note that both DLLs are compiled either for x86 or x64 machines.
Regards
I think I got your problem. Your db connection is OK. First of all you are using which version of dotnetFramework? Accordingly you can download and use System.Data.SQLite.dll file to your references then your problem may be solved. I think you are using a System.Data.SQLite.dll file that is older one (doesn't match your version of dot net).

How to change filetype association in the registry?

first time posting in StackOverflow. :D
I need my software to add a couple of things in the registry.
My program will use
Process.Start(#"blblabla.smc");
to launch a file, but the problem is that most likely the user will not have a program set as default application for the particular file extension.
How can I add file associations to the WindowsRegistry?
In addition to the answers already provided, you can accomplish this by calling the command line programs "ASSOC" and "FTYPE". FTYPE associates a file type with a program. ASSOC associates a file extension with the file type specified through FTYPE. For example:
FTYPE SMCFile="C:\some_path\SMCProgram.exe" -some_option %1 %*
ASSOC .smc=SMCFile
This will make the necessary entries in the registry. For more information, type ASSOC /? or FTYPE /? at the command prompt.
Use the Registry class in Microsoft.Win32.
Specifically, you want the ClassesRoot property of Registry to access the HKEY_CLASSES_ROOT key (cf. Understanding MS Windows File Associations and HKEY_CLASSES_ROOT: Core Services).
using Microsoft.Win32;
Registry
.ClassesRoot
.CreateSubKey(".smc")
.SetValue("", "SMC", RegistryValueKind.String);
Registry
.ClassesRoot
.CreateSubKey("SMC\shell\open\command")
.SetValue("", "SMCProcessor \"%1\"", RegistryValueKind.String);
Replace "SMCProcessor \"%1\"" with the command-line path and argument specification for the program that you wish to associate with files with extension .smc.
But, instead of messing with the registry, why not just say
Process.Start("SMCProcessor blblabla.smc");
Using Python:
EXT, EXT_TYPE = ".xyz", "XYZ file"
EXE_PATH = r"path\to\my\exe"
# %L is the long (full path) version of path
extCmd = '"%s" "%%L" %%*' % EXE_PATH
# Using assoc and ftype easier than editing registry!
assert os.system('assoc %s=%s' % (EXT, EXT_TYPE))==0
assert os.system('ftype %s=%s' % (EXT_TYPE, extCmd))==0
Associating an icon with the extension type:
import _winreg
try:
ext = _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, EXT_TYPE)
_winreg.SetValue(ext, "DefaultIcon", _winreg.REG_SZ, ICON_PATH)
_winreg.CloseKey(ext)
except WindowsError:
print "Error associating icon"
Register the extension as an executable type (i.e. PATHEXT):
try:
key = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
reg = _winreg.ConnectRegistry( None, _winreg.HKEY_LOCAL_MACHINE )
# get current value
ext = _winreg.OpenKey(reg, key)
pathext = _winreg.QueryValueEx(ext, 'PATHEXT')[0]
if not EXT in pathext:
_winreg.CloseKey(ext)
# modify the current value
ext = _winreg.OpenKey(reg, key, 0, _winreg.KEY_ALL_ACCESS)
pathext += ';' + EXT
_winreg.SetValueEx(ext, 'PATHEXT', 0, _winreg.REG_SZ, pathext)
_winreg.CloseKey(ext)
_winreg.CloseKey(reg)
except WindowsError:
print "Error adding to PATHEXT"
Additionally, to get PATHEXT recognised without logging in again you can update the environment: (thanks to Enthought for this)
def refreshEnvironment():
HWND_BROADCAST = 0xFFFF
WM_SETTINGCHANGE = 0x001A
SMTO_ABORTIFHUNG = 0x0002
sParam = "Environment"
import win32gui
res1, res2 = win32gui.SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, sParam, SMTO_ABORTIFHUNG, 100)
If you are planning on providing an installer for your application, simply use the file association feature available in whatever installer framework you choose to use - even the Visual Studio setup project knows how to do this.
To alter file type associations directly from your code, I believe you have to look into HKEY_CLASSES_ROOT and find/create a key with the extension you want to bind to (ie ".pdf"). Within this key, the default value is a string containing a reference to another key within HKEY_CLASSES_ROOT. Go follow that pointer, expand/create the shell subkey and add/change your commands here. Look around this area with regedit to get the fealing of how it looks.
I have some C# code in a pet project of mine, which looks up the binding for PDF files and adds an extra option to their context menus. Feel free to have a look.

Categories