Skip to content

Instantly share code, notes, and snippets.

@harvimt
Created March 10, 2014 07:52
Show Gist options
  • Save harvimt/9461046 to your computer and use it in GitHub Desktop.
Save harvimt/9461046 to your computer and use it in GitHub Desktop.
lib7zip minimal c example.
#include <stdio.h>
#include <windows.h>
#include <wchar.h>
#include <initguid.h>
#include "pstdint.h"
DEFINE_GUID(CLSID_CFormat7z,
0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00);
DEFINE_GUID(IID_IInArchive,
0x23170F69, 0x40C1, 0x278A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x60, 0x00, 0x00);
typedef struct {
HRESULT (*QueryInterface) (void*, GUID*, void**);
uint32_t (*AddRef)(void*);
uint32_t (*Release)(void*);
HRESULT (*Read)(void* self, uint8_t *data, uint32_t size, uint32_t *processedSize);
HRESULT (*Seek)(void* self, int64_t offset, uint32_t seekOrigin, uint64_t *newPosition);
} _IInStream_vtable;
typedef struct{
_IInStream_vtable* vtable;
uint32_t num_refs;
FILE* file;
} IInStream;
typedef struct {
HRESULT (*QueryInterface) (void*, GUID*, void**);
uint32_t (*AddRef)(void*);
uint32_t (*Release)(void*);
HRESULT(*SetTotal)(void* self, const uint64_t *files, const uint64_t *bytes);
HRESULT(*SetCompleted)(void* self, const uint64_t *files, const uint64_t *bytes);
} _IArchiveOpenCallback_vtable;
typedef struct {
_IArchiveOpenCallback_vtable* vtable;
uint32_t num_refs;
} IArchiveOpenCallback;
typedef void IArchiveExtractCallback; /* FIXME */
typedef struct {
HRESULT (*QueryInterface) (void*, GUID*, void**);
uint32_t (*AddRef)(void*);
uint32_t (*Release)(void*);
HRESULT (*Open)(void* self, IInStream *stream, const uint64_t *maxCheckStartPosition, IArchiveOpenCallback *openArchiveCallback);
HRESULT (*Close)(void* self);
HRESULT (*GetNumberOfItems)(void* self, uint32_t *numItems);
HRESULT (*GetProperty)(void* self, uint32_t index, PROPID propID, PROPVARIANT *value);
HRESULT (*Extract)(void* self, const uint32_t* indices, uint32_t numItems, uint32_t testMode, IArchiveExtractCallback *extractCallback);
HRESULT (*GetArchiveProperty)(void* self, PROPID propID, PROPVARIANT *value);
HRESULT (*GetNumberOfProperties)(void* self, uint32_t *numProperties);
HRESULT (*GetPropertyInfo)(void* self, uint32_t index, wchar_t **name, PROPID *propID, VARTYPE *varType);
HRESULT (*GetNumberOfArchiveProperties)(void* self, uint32_t *numProperties);
HRESULT (*GetArchivePropertyInfo)(void* self, uint32_t index, wchar_t **name, PROPID *propID, VARTYPE *varType);
} _IInArchive_vtable;
typedef struct{
_IInArchive_vtable* vtable;
} IInArchive;
typedef HRESULT (__cdecl* _GetNumberOfFormats)(uint32_t *);
typedef HRESULT (__cdecl* _GetNumberOfMethods)(uint32_t *);
typedef HRESULT (__cdecl* _GetMethodProperty)(uint32_t index, PROPID propID, PROPVARIANT * value);
typedef HRESULT (__cdecl* _GetHandlerProperty2)(uint32_t, PROPID propID, PROPVARIANT *);
typedef HRESULT (__cdecl* _CreateObject)(const GUID *, const GUID *, void **);
#define kpidPath 3
#define kpidCRC 19
/** IInStream impl **/
HRESULT IInStream_QueryInterface(IInStream* self, GUID* iid, void** out_obj){
puts("IInStream.QueryInterface");
*out_obj = NULL;
return E_NOINTERFACE;
}
uint32_t IInStream_AddRef(IInStream* self){
self->num_refs += 1;
printf("IInStream.AddRef: num_refs=%u\n", self->num_refs);
return self->num_refs;
}
uint32_t IInStream_Release(IInStream* self){
self->num_refs -= 1;
printf("IInStream.Release: num_refs=%u\n", self->num_refs);
return self->num_refs;
}
HRESULT IInStream_Read(IInStream* self, uint8_t *data, uint32_t size, uint32_t *processedSize){
puts("IInStream.Read");
if(processedSize != NULL){
*processedSize = fread(data, sizeof(uint8_t), size, self->file);
}else{
fread(data, sizeof(uint8_t), size, self->file);
}
return S_OK;
}
HRESULT IInStream_Seek(IInStream* self, int64_t offset, uint32_t seekOrigin, uint64_t *newPosition){
puts("IInStream.Seek");
if(newPosition != NULL){
*newPosition = _fseeki64(self->file, offset, seekOrigin);
}else{
_fseeki64(self->file, offset, seekOrigin);
}
return S_OK;
}
/** IOpenArchiveCallback impl **/
HRESULT IOpnArc_QueryInterface(IArchiveOpenCallback* self, GUID* iid, void** out_obj){
puts("IOpenArchiveCallback.QueryInterface");
*out_obj = NULL;
return E_NOINTERFACE;
}
uint32_t IOpnArc_AddRef(IArchiveOpenCallback* self){
self->num_refs += 1;
printf("IArchiveOpenCallback.AddRef: num_refs=%u\n", self->num_refs);
return self->num_refs;
}
uint32_t IOpnArc_Release(IArchiveOpenCallback* self){
self->num_refs -= 1;
printf("IArchiveOpenCallback.Release: num_refs=%u\n", self->num_refs);
return self->num_refs;
}
HRESULT IOpnArc_SetTotal(IArchiveOpenCallback* self, const uint64_t *files, const uint64_t *bytes){
puts("IArchiveOpenCallback.SetTotal");
return S_OK;
}
HRESULT IOpnArc_SetCompleted(IArchiveOpenCallback* self, const uint64_t *files, const uint64_t *bytes){
puts("IArchiveOpenCallback.SetCompleted");
return S_OK;
}
int testmain(){
HMODULE lib;
_GetNumberOfFormats GetNumberOfFormats;
_GetHandlerProperty2 GetHandlerProperty2;
_CreateObject CreateObject;
IArchiveOpenCallback callback;
_IArchiveOpenCallback_vtable callback_vt;
IInStream in_stream;
_IInStream_vtable in_stream_vt;
uint64_t maxCheckStartPosition = 0;
IInArchive* archive;
uint32_t num_formats = 0;
puts("Starting");
lib = LoadLibrary("C:\\Program Files\\7-Zip\\7z.dll");
if(lib == NULL){
lib = LoadLibrary("C:\\Program Files (x86)\\7-Zip\\7z.dll");
if(lib == NULL){
puts("lib is NULL");
return 0;
}
}
GetNumberOfFormats = (_GetNumberOfFormats) GetProcAddress(lib, "GetNumberOfFormats");
if(GetNumberOfFormats == NULL){
puts("NULL FAIL");
return 0;
}
GetHandlerProperty2 = (_GetHandlerProperty2) GetProcAddress(lib, "GetHandlerProperty2");
if(GetHandlerProperty2 == NULL){
puts("NULL FAIL");
return 0;
}
CreateObject = (_CreateObject) GetProcAddress(lib, "CreateObject");
if(CreateObject == NULL){
puts("NULL FAIL");
return 0;
}
/** Setup COM Structs **/
in_stream_vt.QueryInterface = &IInStream_QueryInterface;
in_stream_vt.AddRef = &IInStream_AddRef;
in_stream_vt.Release = &IInStream_Release;
in_stream_vt.Read = &IInStream_Read;
in_stream_vt.Seek = &IInStream_Seek;
in_stream.vtable = &in_stream_vt;
in_stream.num_refs = 1;
in_stream.file = fopen("test.7z", "rb");
callback.vtable = &callback_vt;
callback_vt.QueryInterface = &IOpnArc_QueryInterface;
callback_vt.AddRef = &IOpnArc_AddRef;
callback_vt.Release = &IOpnArc_Release;
callback_vt.SetTotal = &IOpnArc_SetTotal;
callback_vt.SetCompleted = &IOpnArc_SetCompleted;
callback.num_refs = 1;
/** Run **/
if(GetNumberOfFormats(&num_formats) != 0){
puts("FAIL!");
return 0;
}
printf("num_formats=%u\n", num_formats);
if(CreateObject(&CLSID_CFormat7z, &IID_IInArchive, (void**)&archive) != S_OK){
puts("CreateObject Fail");
goto free;
}
if(archive == NULL){
puts("archive is NULL after CreateObject");
goto free;
}
if(archive->vtable->Open(archive, &in_stream, &maxCheckStartPosition, &callback) != S_OK){
puts("Open Fail");
goto free;
}
{
uint32_t num_items = 0;
uint32_t i;
PROPVARIANT path;
PROPVARIANT crc;
if(archive->vtable->GetNumberOfItems(archive, &num_items) != S_OK){
puts("archive.GetNumberOfItems FAIL");
goto close;
}
for(i = 0; i < num_items; i += 1){
PropVariantInit(&path);
PropVariantInit(&crc);
archive->vtable->GetProperty(archive, i, kpidPath, &path);
archive->vtable->GetProperty(archive, i, kpidCRC, &crc);
if(crc.vt != VT_UI4){
puts("crc has wrong vt");
goto clear;
}
if(path.vt != VT_BSTR){
puts("path has wrong vt");
goto clear;
}
printf("path=%ls; crc=%x\n", path.bstrVal, crc.ulVal);
clear:
PropVariantClear(&path);
PropVariantClear(&crc);
}
}
close:
if(archive->vtable->Close(archive) != S_OK){
puts("Close Fail");
}
free:
puts("SOMETHING HAPPENED");
puts(FreeLibrary(lib) ? "Free Successful" : "Free Unsuccessful");
return 0;
}
from cffi import FFI
ffi = FFI()
ffi.cdef("""
void testmain();
""")
with open("cffi_test.c", 'r') as f:
C = ffi.verify(
f.read()
, libraries=["ole32"], include_dirs=["."])
print('VERIFICATION COMPLETE');
C.testmain()
print('SOMETHING DEFINITELY HAPPENED');
@cielavenir
Copy link

cielavenir commented Aug 11, 2019

Based on your idea, I have written C interface to 7z.so [edit: compatible with both WIndows and Linux/OSX]. Thank you so much.

https://gist.github.com/cielavenir/099bb17e673c734c1c13790835f77206

@hdbreaker
Copy link

Hi @harvimt, I found this example incredible due that the almost nonexistent documentation about 7z.dll library.

I'm having an issue in :

if (archive->vtable->Open(archive, &in_stream, &maxCheckStartPosition, &callback) != S_OK) {} 

The application crash with: 0xC0000005: Access violation executing location 0x00000000.

archive => 0x007028a8 (valid pointer with accessible vtable)
in_stream => 0x0023f770 (valid pointer)
maxCheckStartPosition => 0 (valid pointer to uint64_t)
callback => 0x0023f7bc (valid pointer)

Do you know why this could be happening? I really appreciate your help!

Screen Shot 2020-04-22 at 16 39 01

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment