#define UNICODE
#define _UNICODE
#include <windows.h>
#include <windowsx.h>
#include <shlwapi.h>
#include <commctrl.h>
#include <malloc.h>
#include <stdio.h>
#include <tchar.h>
#include <richedit.h>
#pragma comment(linker,"/NOD /OPT:NOWIN98")
#pragma intrinsic(memset)
#define elemof(x) (sizeof(x)/sizeof(*(x)))
enum{
MB_SOUND =0x80000000,
};
static HINSTANCE hInstance;
static HWND hMainWnd,hDialog;
static HWND hToolbar,hEdit,hStatus;
static HWND hFind;
static UINT msgFind;
static FINDREPLACE findreplace;
static TCHAR findstr[80],replacestr[80];
static HMENU hMainMenu;
static TCHAR MBoxTitle[64];
static TCHAR ExeName[MAX_PATH];
static TCHAR BakName[MAX_PATH];
static TCHAR CustFilter[64];
static int filetype; // 1 = Exe, 2 = UTF8-Text
static int vMBox(HWND parent,ULONG_PTR id, UINT flags, va_list va) {
TCHAR s[256],tbuf[256],*t=tbuf;
if (IS_INTRESOURCE(id)) LoadString(hInstance,(UINT)id,tbuf,elemof(tbuf));
else t=(TCHAR*)id;
s[_vsntprintf(s,elemof(s)-1,t,va)]=0;
if (flags&MB_SOUND) MessageBeep(flags&MB_ICONMASK);
return MessageBox(parent,s,MBoxTitle,flags&~MB_SOUND);
}
static int _cdecl MBox(HWND parent,ULONG_PTR id,UINT flags,...) {
va_list va;
va_start(va,flags);
int r=vMBox(parent,id,flags,va);
va_end(va);
return r;
}
template<class T> int StrReplace(T*s,int slen,int buflen,int idel, int ldel, const T*sins,int lins);
static int StrReplace<char>(char*s,int slen,int buflen,int idel, int ldel, const char*sins,int lins) {
if (buflen<4) return 0;
if (slen<0) slen=lstrlenA(s)+1;
if ((unsigned)idel>=(unsigned)slen) return 0;
if (lins<0) lins=lstrlenA(sins);
int move=lins-ldel; // amount of grow
if ((unsigned)(slen+move)>(unsigned)buflen) return 0;
MoveMemory(s+idel+lins,s+idel+ldel,slen-idel-ldel);
CopyMemory(s+idel,sins,lins);
return slen+move;
}
static int StrReplace<WCHAR>(WCHAR*s,int slen,int buflen,int idel, int ldel, const WCHAR*sins,int lins) {
if (buflen<4) return 0;
if (slen<0) slen=lstrlenW(s)+1;
if ((unsigned)idel>=(unsigned)slen) return 0;
if (lins<0) lins=lstrlenW(sins);
int move=lins-ldel; // amount of grow
if ((unsigned)(slen+move)>(unsigned)buflen) return 0;
MoveMemory(s+idel+lins,s+idel+ldel,(slen-idel-ldel)*2);
CopyMemory(s+idel,sins,lins*2);
return slen+move;
}
struct Node{
static HANDLE hHeap;
enum type_t{ //scope sub name value
root, //0..len PI,Element,Comment 0 0
Element, //<tag..> Attr;Element,Text,Comm. tag 0 if collapsed, between > and </ when split
Attr, //tag="value" 0 tag value
Text, //text Entity 0 text without surrounding whitespace
CDATASection, //<![CDATA[var]]> 0 0 var
EntityReference, //?
Entity, //&tag; 0 tag 0
ProcessingInstruction,//<?tag ?> Attr tag between tag and ?>
Comment, //<!-- --> 0 0 between <!-- and -->
Document, //HTML only
DocumentType, //<!DOCTYPE >
DocumentFragment, //HTML only
Notation //?
}type;
int depth;
CHARRANGE scope,name,value;
Node*next,*prev,*parent,*sub;
static void*operator new(size_t sz) {return HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sz);}
static void operator delete(void*) {} // simply don't delete
Node(type_t t=root):type(t),depth(0) {next=prev=this;}
// Node(type_t t,int a, int e):type(t),depth(0) {next=prev=this; scope.cpMin=a; scope.cpMax=e;}
// int build(int);
// Node*&lastsub() {if (sub) {Node**p=⊂ while ((*p)->next) p=&((*p)->next); return *p;} return sub;}
void add_child_back(Node*n) {if (sub) sub->add_sibling_back(n); else{sub=n;n->parent=this;n->depth=depth+1;}}
// Node*last() {Node*p=this; while (p->next) p=p->next; return p;}
void add_sibling_back(Node*n) {n->parent=parent; n->next=this; prev->next=n; n->prev=prev; prev=n;n->depth=depth;}
void add_sibling_front(Node*n) {add_sibling_back(n); n->parent->sub=n;}
bool isChild(Node*n) const {return n->parent==this;}
};
struct Xml {
Node*root;
char*buf; // UTF-8
int len,bufsize;
// Unsauber: Wie bekommt man ein operator new mit hHeap in Xml hin??
Xml() {Node::hHeap=HeapCreate(HEAP_NO_SERIALIZE,0,0);root=new Node;}
Xml(HWND,int=0);
int parse(); // linearisierte Version!
Node*addAttr(Node*p,const char*tag,int tlen,const char*val,int vlen=-1,Node*before=0);
Node*addAttr(Node*p,const char*tag,const char*val,Node*before=0) {return addAttr(p,tag,-1,val,-1,before);}
Node*addElement(Node*p,const char*tag,int tlen=-1,Node*before=0/*,Node::type_t t=Node::Element*/);
Node*setElement(Node*p,const char*tag,bool* =0); // Kind von p finden oder anhängen
Node*addPI(const char*tag,int tlen=-1,Node*before=0); // always to root node
Node*addText(Node*p,const char*txt,int tlen=-1,Node*before=0);
void splitElement(Node*el); // prepare for text and subelement insertion
void unsplitElement(Node*el); // after text and subelement deletion
bool deleteNode(Node*n);
void show(HWND) const;
bool shift(int from,int by); // Text ab Index um Delta (einfügen, löschen) sowie Nodes anpassen
bool parseAttr(Node*el,int&i);
~Xml() {HeapDestroy(Node::hHeap);}
int compareText(CHARRANGE&t1,CHARRANGE&t2) const {return compareText(t1,t2.cpMin,t2.cpMax);}
int compareText(CHARRANGE&t1,int a,int e) const {return compareText(t1,buf+a,e-a);}
int compareText(CHARRANGE&t1,const char*t2) const {return compareText(t1,t2,strlen(t2));}
int compareText(CHARRANGE&t1,const char*t2,int len) const {return compareText(buf+t1.cpMin,t1.cpMax-t1.cpMin,t2,len);}
static int compareText(const char*t1,int l1,const char*t2,int l2);
Node*findElement(Node*r,const char*tag) const {return findNode(r,Node::Element,tag);}
Node*findNode(Node*r,Node::type_t t,const char*tag=0) const;
bool enumNodes(Node*r,Node::type_t t,const char*tag,bool(*)(const Xml&,Node*,void*),void*) const;
Node*findChildNode(Node*p,Node::type_t t,const char*tag,int tlen=-1) const;
private:
static bool stop_and_save(const Xml&,Node*n,void*param) {*(Node**)param=n; return false;}
struct shift_t{
int from,by;
operator()(CHARRANGE&r) {if (r.cpMin>=from) r.cpMin+=by; if (r.cpMax>from) r.cpMax+=by;}
};
static bool onShift(const Xml&,Node*n,void*param);
};
int Xml::compareText(const char*t1,int l1,const char*t2,int l2){
if (l1!=l2) return -1;
return memcmp(t1,t2,l1);
}
bool Xml::onShift(const Xml&,Node*n,void*param) {
shift_t&sh=*(shift_t*)param;
sh(n->scope);
sh(n->name);
sh(n->value);
return true;
}
bool Xml::shift(int from,int by) {
if ((unsigned)from>(unsigned)len) return false; // start index too large
if (!by) return true; // do nothing
if (len+by>bufsize) return false; // expansion too large
if (len+by<from) return false; // shrink too much: New <len> would be less than <from>
if (by<0) memmove(buf+from,buf+from-by,len-from+by);
else{
memmove(buf+from+by,buf+from,len-from);
#ifdef _DEBUG
memset(buf+from,'X',by); // fill gap
#endif
}
len+=by;
shift_t sh={from,by};
enumNodes(root,Node::type_t(-1),0,onShift,&sh);
return true;
}
// el = Element node newly created
// i = position in buf (onto whitespace or ">" or "/>" or "?>")
// On return: i onto ">" or "/>" or "?>" or erraneous character
// attribute nodes added to el->sub
bool Xml::parseAttr(Node*el,int&i) {
while (i<len) {
if (buf[i]<=' ') {++i; continue;} // skip whitespace
switch (buf[i]) {
case '>': return true;
case '?':
case '/': return i<len-1 && buf[i+1]=='>';
}
Node*k=new Node(Node::Attr);
k->scope.cpMin=k->name.cpMin=i;
while (i<len) {
char c=buf[i];
if (c>' ' && c!='=' && c!='>' && c!='?') {++i; continue;}
k->name.cpMax=i;
if (c=='=') {
char q=buf[++i];
if (q!='"' && q!='\'') return false; // must be in single or double quotes for XML, not for HTML
k->value.cpMin=++i;
for(;i<len && buf[i]!=q;i++);
if (i==len) return false; // end of input without closing quote
k->value.cpMax=i++;
}
k->scope.cpMax=i;
break;
}
el->add_child_back(k);
}
return false; // end of input without ">"
}
int Xml::parse() {
root->scope.cpMax=len;
Node*cur=root;
for (int i=0; i<len;) {
if (buf[i]<=' ') {++i; continue;}
if (buf[i]=='<') switch (buf[++i]) {
case '/': {
if (cur->type!=Node::Element) return i;
int k=++i; // save start of string
while (i<len && buf[i]>' ' && buf[i]!='>') ++i; // locate end of tag
if (k!=i // Not only "</>"
&& compareText(cur->name,k,i)) return k; // Error! Tag must be the same
cur->value.cpMax=k-2; // where “innerHTML” ends
while (i<len && buf[i]!='>') ++i; // locate end marker (Bug: skipping false attributes)
if (i==len) return i; // No end marker
cur->scope.cpMax=++i; // now end of scope available
cur=cur->parent; // ascend
}break;
case '?': {
Node*n=new Node(Node::ProcessingInstruction);
n->scope.cpMin=i-1;
n->name.cpMin=++i;
for(;i<len;i++) {
char c=buf[i];
if (c<=' ' || c=='>' || c=='/' || c=='?') break;
}
n->name.cpMax=i;
if (!parseAttr(n,i)) return i;
if (buf[i]!='?') return i; // wrong termination
n->scope.cpMax=(i+=2);
cur->add_child_back(n);
}break;
case '!': {
Node*n=new Node(Node::Comment);
n->scope.cpMin=i-1;
if (i>len-6) return i; // no space for "-- -->"
if (buf[++i]!='-') return i;
if (buf[++i]!='-') return i;
n->value.cpMin=++i;
for (; i<len-2; i++) {
if (buf[i]=='-' && buf[i+1]=='-' && buf[i+2]=='>') break;
}
if (i==len-2) return i; // missing termination
n->value.cpMax=i;
n->scope.cpMax=(i+=3);
cur->add_child_back(n);
}break;
default: {
Node*n=new Node(Node::Element);
n->scope.cpMin=i-1;
n->name.cpMin=i;
for(;i<len;i++) {
char c=buf[i];
if (c<=' ' || c=='>' || c=='/' || c=='?') break;
}
n->name.cpMax=i;
if (!parseAttr(n,i)) return i;
char c=buf[i];
if (c=='?') return i; // not allowed end marker
cur->add_child_back(n);
if (c=='/') {
n->scope.cpMax=(i+=2);
break;
}
n->value.cpMin=++i; // where “innerHTML” starts
cur=n; // descend
}
}else{
if (cur->type!=Node::Element) return i; // root-level text not allowed
Node*n=new Node(Node::Text);
n->scope.cpMin=i;
while (i<len && (buf[i]<=' ' || buf[i]=='<')) ++i;
n->value.cpMin=i;
while (i<len && buf[i]!='<') ++i;
n->scope.cpMax=i;
int j=i;
while (buf[--j]<=' ');
n->value.cpMax=max(j+1,n->value.cpMin);
cur->add_child_back(n);
}
}
if (cur->type!=Node::root) return i; // Not at root at end of input
return 0; // okay
}
Node*Xml::findNode(Node*r,Node::type_t t,const char*tag) const{
Node*ret=0;
enumNodes(r,t,tag,stop_and_save,&ret);
return ret;
}
bool Xml::enumNodes(Node*r,Node::type_t t,const char*tag,bool(*cb)(const Xml&,Node*,void*),void*param) const{
if (!r) return true;
Node*n=r;
do{
if ((t==-1 || n->type==t) && (!tag || !compareText(n->name,tag)) && !cb(*this,n,param)) return false;
if (!enumNodes(n->sub,t,tag,cb,param)) return false; // recurse through sub-tree
n=n->next;
}while (n!=r); // stop when all siblings are checked
return true;
}
Node*Xml::findChildNode(Node*p,Node::type_t t,const char*tag,int tlen) const{
if (tlen<0) tlen=strlen(tag);
Node*n0=p->sub,*n=n0;
if (!n0) return 0;
do{
if ((t==-1 || n->type==t) && (!compareText(n->name,tag,tlen))) return n;
n=n->next;
}while (n!=n0); // stop when all siblings are checked
return 0;
}
HANDLE Node::hHeap;
Xml::Xml(HWND hEdit,int bs) {
Node::hHeap=HeapCreate(HEAP_NO_SERIALIZE,0,0);
root=new Node;
int lw=SendMessage(hEdit,WM_GETTEXTLENGTH,0,0);
TCHAR*sw=(TCHAR*)_alloca((lw+1)*sizeof(TCHAR));
GetWindowText(hEdit,sw,lw+1);
len=WideCharToMultiByte(CP_UTF8,0,sw,lw,0,0,0,0);
if (bs<len) bs=len;
bufsize=bs;
buf=(char*)Node::operator new(bs); // nicht nullterminiert!
WideCharToMultiByte(CP_UTF8,0,sw,lw,const_cast<char*>(buf),len,0,0);
// if (bs>len) buf[len]=0; // nullterminiert wenn Platz
}
void Xml::show(HWND hEdit) const{
int lw=MultiByteToWideChar(CP_UTF8,0,buf,len,0,0);
TCHAR*sw=(TCHAR*)_alloca((lw+1)*sizeof(TCHAR));
MultiByteToWideChar(CP_UTF8,0,buf,len,sw,lw);
sw[lw]=0;
Edit_SetModify(hEdit,true);
SetWindowText(hEdit,sw); // EN_CHANGE enabled dann den Apply-Menüpunkt - denkste!! Nicht bei RichEdit!
Edit_SetModify(hEdit,true);
}
bool Xml::deleteNode(Node*n) {
if (n->type==Node::root) return false;
int a=n->scope.cpMin;
int e=n->scope.cpMax;
if (buf[e]==' ') ++e;
if (buf[e]=='\r') ++e;
if (buf[e]=='\n') ++e;
Node*prv=n->prev,*nxt=n->next;
prv->next=nxt; // may write to same node when there is only one sibling
nxt->prev=prv;
if (n->parent->sub==n) n->parent->sub=nxt!=n?nxt:0;
delete n;
return shift(a,a-e);
}
//Leeres Elternelement der Form <tag/>\n oder <tag attr="value"/>\n aufteilen in <tag>\n</tag>\n bzw. <tag attr="value">\n</tag>\n.
//Dann erst kann das Kindelement (außer Attr) eingefügt werden.
void Xml::splitElement(Node*el) {
if (el->type!=Node::Element) return;
if (el->value.cpMin) return; // already split (value is “innerHTML”)
int a=el->scope.cpMax-2; // onto "/>"
int b=el->name.cpMax-el->name.cpMin;
if (!shift(a,b+3)) return; // make gap, modifying el->scope.cpMax
_snprintf(buf+a,b+5,"> </%.*s>",b,buf+el->name.cpMin);
el->value.cpMin=a+1; // onto " "
el->value.cpMax=a+2; // onto "</tag>"
}
void Xml::unsplitElement(Node*el) {
if (el->type!=Node::Element) return;
if (!el->value.cpMin) return; // already unsplit
Node*q,*p=el->sub;
if (q=p) do{
if (q->type!=Node::Attr) return; // has non-attribute child
}while((q=q->next)!=p); // until end of list
int a=el->value.cpMin+1; // one character after ">": Let space for "/>"
int b=el->scope.cpMax-a;
shift(a,-b);
_snprintf(buf+a-2,2,"/>");
el->value.cpMax=el->value.cpMin=0; // mark as unsplit form
}
Node*Xml::addElement(Node*p,const char*tag,int tlen,Node*before) {
if (!p) p=root;
if (!(p->type==Node::Element || p->type==Node::root)) return 0; // parent must be element or root
if (before) {
if (before->parent!=p) return 0; // <before> must be direct child of <p>
if (before->type==Node::Attr) return 0; // cannot be an attribute
}
if (tlen<0) tlen=strlen(tag);
splitElement(p);
int a=before?before->scope.cpMin:p->sub&&p->sub->prev->type!=Node::Attr?p->sub->prev->scope.cpMax:p->value.cpMax;
if (!a) return 0;
int b=tlen+5;
if (!shift(a,b)) return 0;
_snprintf(buf+a,b,"\n<%.*s/>\n",tlen,tag); // create an unsplit element in a new line
Node*n=new Node(Node::Element);
n->scope.cpMin=a+1;
n->scope.cpMax=a+b-2;
n->name.cpMin=a+2;
n->name.cpMax=a+2+tlen;
if (before) before->add_sibling_back(n);
else p->add_child_back(n);
return n;
}
Node*Xml::setElement(Node*p,const char*tag,bool*created) {
if (created) *created=false;
Node*n=findChildNode(p,Node::Element,tag); // wenn's da ist, liefern
if (n) return n;
if (p==root) {
Node*pi=addPI("xml",3,p->sub); // am Anfang
addAttr(pi,"version","1.0");
addAttr(pi,"encoding","UTF-8");
addAttr(pi,"standalone","yes");
}
if (created) *created=true;
return addElement(p,tag); // wenn nicht, erzeugen und (innen) anhängen
}
Node*Xml::addPI(const char*tag,int tlen,Node*before) {
if (before && before->parent!=root) return 0; // <before> must be direct child of root
if (tlen<0) tlen=strlen(tag);
int a=before?before->scope.cpMin:0;
int b=tlen+6;
if (!shift(a,b)) return 0;
_snprintf(buf+a,b,"<?%.*s ?>\n",tlen,tag); // create a processing instruction in a new line
Node*n=new Node(Node::ProcessingInstruction);
n->scope.cpMin=a;
n->scope.cpMax=a+b-1;
n->name.cpMin=a+2;
n->name.cpMax=a+2+tlen;
if (before) before->add_sibling_back(n);
else root->add_child_back(n);
return n;
}
Node*Xml::addText(Node*p,const char*txt,int tlen,Node*before) {
if (!p) return 0;
if (p->type!=Node::Element) return 0;
if (before) {
if (before->parent!=p) return 0; // <before> must be direct child of <p>
if (before->type==Node::Attr) return 0; // cannot be an attribute
}
// TODO: Check whether insertion point is not surrounded by text nodes, then modify text node instead
splitElement(p);
int a=before?before->scope.cpMin:p->sub?p->sub->prev->scope.cpMax:p->value.cpMin;
if (!a) return 0; // something wrong
if (!shift(a,tlen)) return 0;
_snprintf(buf+a,tlen,"%.*s",tlen,txt); // dasselbe wie memcpy(buf+a,txt,tlen)
Node*n=new Node(Node::Text);
n->scope.cpMin=n->value.cpMin=a;
n->scope.cpMax=n->value.cpMax=a+tlen;
if (before) before->add_sibling_back(n);
else p->add_child_back(n);
return n;
}
Node*Xml::addAttr(Node*el,const char*tag,int tlen,const char*val,int vlen,Node*before) {
if (!el) return 0;
if (el->type!=Node::Element) return 0;
if (before) {
if (before->parent!=el) return 0;
if (before->type!=Node::Attr) return 0;
}
Node*lastattr=0;
if (!before) {
Node*q,*p=el->sub;
if (q=p) do{
if (q->type!=Node::Attr) break;
lastattr=q;
}while((q=q->next)!=p);
}
int a=before?before->scope.cpMin-1:lastattr?lastattr->scope.cpMax:el->name.cpMax;
if (!a) return 0;
if (tlen<0) tlen=strlen(tag);
if (vlen<0) vlen=strlen(val);
int b=tlen+vlen+4;
if (!shift(a,b)) return 0;
_snprintf(buf+a,b," %.*s=\"%.*s\"",tlen,tag,vlen,val);
Node*n=new Node(Node::Attr);
n->scope.cpMin=n->name.cpMin=a+1;
n->name.cpMax=a+1+tlen;
n->value.cpMin=a+1+tlen+2;
n->value.cpMax=a+b-1;
n->scope.cpMax=a+b;
if (before) before->add_sibling_back(n);
else if (lastattr) lastattr->add_sibling_front(n);
else el->add_child_back(n);
return n;
}
static bool addDependency(Xml&xml, const char*token, const char*name, const char*version) {
bool created;
Node*a=xml.setElement(xml.root,"assembly",&created);
if (created) {
xml.addAttr(a,"xmlns","urn:schemas-microsoft-com:asm.v1");
xml.addAttr(a,"manifestVersion","1.0");
}
Node*b=xml.setElement(a,"dependency");
Node*c=xml.addElement(b,"dependentAssembly"); // immer neu
Node*d=xml.addElement(c,"assemblyIdentity");
xml.addAttr(d,"type","win32");
xml.addAttr(d,"name",name);
xml.addAttr(d,"version",version);
xml.addAttr(d,"processorArchitecture","*");
xml.addAttr(d,"publicKeyToken",token);
xml.addAttr(d,"language","*");
return true;
}
static bool removeDependency(Xml&xml, const char*token) {
Node*a=xml.findChildNode(xml.root,Node::Element,"assembly");
if (!a) return false;
Node*b=xml.findChildNode(a,Node::Element,"dependency");
if (!b) return false;
Node*d=xml.findNode(b,Node::Attr,"publicKeyToken"); // TODO!!
return true;
}
static bool onNode(const Xml&xml,Node*n,void*) {
Node*a=xml.findNode(n,Node::Attr,"publicKeyToken");
if (a) {
unsigned __int64 tok;
sscanf(xml.buf+a->value.cpMin,"%I64x",&tok);
switch (tok) {
case 0x6595b64144ccf1df: CheckMenuItem(hMainMenu,112,MF_CHECKED); break; // comctl32.dll
case 0x1fc8b3b9a1e18e3b: break; // Microsoft.VC80.CRT
}
}
return true;
}
static void classify() {
Xml xml(hEdit); // construct XML parser object: Get string as UTF-8
xml.parse(); // parse to tree
Node*n=xml.findElement(xml.root,"dpiAware");
unsigned id=309;
if (n) {
Node*t=xml.findNode(n,Node::Text);
if (!xml.compareText(t->value,"true/PM",4)) id=310;
else if (!xml.compareText(t->value,"true/PM",7)) id=311;
}
CheckMenuRadioItem(hMainMenu,309,311,id,0);
n=xml.findElement(xml.root,"activeCodePage");
CheckMenuItem(hMainMenu,115,n?MF_CHECKED:MF_UNCHECKED);
xml.enumNodes(xml.root,Node::Element,"assemblyIdentity",onNode,0);
}
static void setDpiAware(char sel) {
Xml xml(hEdit,4096); // construct XML parser object: Get string as UTF-16
xml.parse(); // parse to tree
if (sel) {
Node*n=xml.findElement(xml.root,"dpiAware");
if (n) {
xml.deleteNode(n->sub);
}else{
Node*a=xml.findElement(xml.root,"assembly");
Node*b=xml.addElement(a,"asmv3:application");
xml.addAttr(b,"xmlns:asmv3","urn:schemas-microsoft-com:asm.v3");
Node*c=xml.addElement(b,"asmv3:windowsSettings");
xml.addAttr(c,"xmlns","http://schemas.microsoft.com/SMI/2005/WindowsSettings");
n=xml.addElement(c,"dpiAware");
}
xml.addText(n,"true/PM",sel==2?7:4);
}else{
xml.deleteNode(xml.findElement(xml.root,"asmv3:application"));
}
xml.show(hEdit);
}
static void indent() {
}
static bool setWindowTextU(HWND hEdit,const char*s,int slen=-1) {
if (slen<0) slen=strlen(s);
int wl=MultiByteToWideChar(CP_UTF8,0,s,slen,0,0);
WCHAR*ws=(WCHAR*)_alloca((wl+1)*2);
ws[MultiByteToWideChar(CP_UTF8,0,s,slen,ws,wl)]=0;
return !!SetWindowText(hEdit,ws);
}
static bool getmanifest(HMODULE hExe) {
// if (!hExe) {
// MBox(hMainWnd,2,MB_OK,ExeName);
// return -2;
// }
HRSRC hRes=FindResource(hExe,MAKEINTRESOURCE(1),RT_MANIFEST);
if (!hRes) return false;
HGLOBAL h1=LoadResource(hExe,hRes);
if (!h1) return false;
const char*m=(const char*)LockResource(h1);
if (!m) return false;
int slen=SizeofResource(hExe,hRes);
const char*z=reinterpret_cast<const char*>(memchr(m,0,slen));
if (z) MBox(hMainWnd,10,MB_OK,z-m);
Edit_SetModify(hEdit,false);
setWindowTextU(hEdit,m,slen);
UnlockResource(h1);
classify();
return true;
}
static bool readmanifest() {
HANDLE f=CreateFile(ExeName,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
if (f==INVALID_HANDLE_VALUE) return false;
struct Filesize{
DWORD lo,hi;
operator unsigned __int64() const {return *reinterpret_cast<const unsigned __int64*>(this);}
void operator()(HANDLE f) {lo=GetFileSize(f,&hi);}
}size;
size(f);
if (size>4096) {CloseHandle(f); return false;} // zu groß
filetype=2;
char*t=(char*)_alloca(size.lo);
ReadFile(f,t,size.lo,&size.hi,0);
CloseHandle(f);
if (size.hi!=size.lo) return false;
Edit_SetModify(hEdit,false);
return setWindowTextU(hEdit,t,size.lo);
}
static bool getmanifest() {
HINSTANCE hTest=LoadLibraryEx(ExeName,0,LOAD_LIBRARY_AS_DATAFILE);
if (!hTest) return readmanifest();
filetype=1;
bool b=getmanifest(hTest);
FreeLibrary(hTest);
return b;
}
static bool makebakname() {
lstrcpyn(BakName,ExeName,elemof(BakName));
int i=PathFindExtension(BakName)-BakName;
return StrReplace(BakName,-1,elemof(BakName),i,0,TEXT("~"),-1)>0;
}
static bool revert(HWND parent) {
if (MoveFileEx(BakName,ExeName,MOVEFILE_REPLACE_EXISTING)) {
EnableMenuItem(hMainMenu,104,MF_GRAYED);
getmanifest();
MBox(parent,5,MB_OK|MB_ICONINFORMATION,ExeName);
return true;
}
MBox(parent,6,MB_OK|MB_ICONEXCLAMATION|MB_SOUND,ExeName);
return false;
}
// apply changes
static bool apply(HWND parent) {
// If no backup file exists, create one. Existence is marked in MainMenu id 104.
if (GetMenuState(hMainMenu,104,0)&MF_GRAYED) {
if (CopyFile(ExeName,BakName,TRUE)) {
EnableMenuItem(hMainMenu,104,MF_ENABLED);
}else if (MBox(parent,3,MB_YESNO|MB_ICONQUESTION|MB_SOUND)!=IDYES) return false;
}
// Now use XxxUpdateResource functions to modify the executable file
HANDLE hUpdate=BeginUpdateResource(ExeName,FALSE);
if (!hUpdate) {
MBox(parent,4,MB_OK|MB_ICONEXCLAMATION|MB_SOUND);
return false;
}
int lw=GetWindowTextLength(hEdit)+1;
WCHAR*sw=(WCHAR*)_alloca(lw*2);
GetWindowText(hEdit,sw,lw);
int lu=WideCharToMultiByte(CP_UTF8,0,sw,lw,0,0,0,0);
char*su=(char*)_alloca(lu);
WideCharToMultiByte(CP_UTF8,0,sw,lw,su,lu,0,0);
if (!UpdateResource(hUpdate,RT_MANIFEST,MAKEINTRESOURCE(2),0,su,lu)) {
MBox(parent,4,MB_OK|MB_ICONEXCLAMATION|MB_SOUND);
return false;
}
if (!EndUpdateResource(hUpdate,FALSE)) {
MBox(parent,4,MB_OK|MB_ICONEXCLAMATION|MB_SOUND);
return false;
}
MBox(parent,7,MB_OK|MB_ICONINFORMATION);
EnableMenuItem(hMainMenu,102,MF_GRAYED);
return true;
}
static void handleDependency(UINT id,unsigned __int64 token,const char*name,const char*version) {
char buf[17];
_snprintf(buf,sizeof buf,"%I64x",token);
Xml xml(hEdit,4096);
xml.parse();
UINT check=0;
if (GetMenuState(hMainMenu,id,0)&MF_CHECKED) {
if (!removeDependency(xml,buf)) return;
}else{
if (!addDependency(xml,buf,name,version)) return;
check=MF_CHECKED;
}
CheckMenuItem(hMainMenu,id,check);
xml.show(hEdit);
}
static void handleOS(UINT id, unsigned __int64 guidH, unsigned __int64 guidL) {
char buf[39];
_snprintf(buf,sizeof buf,"{%08x-%04x-%04x-%04x-%012x}",
guidH>>32, guidH>>16&0xFFFF, guidH&0xFFFF,
guidL>>48, guidL&0xFFFFFFFFFFFF);
}
static bool openfile(HWND parent) {
OPENFILENAME ofn;
ZeroMemory(&ofn,sizeof ofn);
ofn.lStructSize=sizeof ofn;
ofn.hwndOwner=parent;
ofn.lpstrFile=ExeName;
ofn.nMaxFile=elemof(ExeName);
ofn.Flags=OFN_HIDEREADONLY|OFN_FILEMUSTEXIST;
ofn.nFilterIndex=filetype;
ofn.lpstrCustomFilter=CustFilter;
ofn.nMaxCustFilter=elemof(CustFilter);
TCHAR f[64];
f[LoadString(hInstance,8,f,elemof(f)-1)+1]=0;
ofn.lpstrFilter=f;
if (!GetOpenFileName(&ofn)) return false;
getmanifest();
makebakname();
BOOL bTest=PathFileExists(BakName);
EnableMenuItem(hMainMenu,103,filetype==1?MF_ENABLED:MF_GRAYED);
EnableMenuItem(hMainMenu,2,MF_ENABLED|MF_BYPOSITION);
EnableMenuItem(hMainMenu,104,bTest?MF_ENABLED:MF_GRAYED);
DrawMenuBar(hMainWnd);
TCHAR s[64];
_sntprintf(s,elemof(s),TEXT("%s - %s"),ExeName+ofn.nFileOffset,MBoxTitle);
SetWindowText(hMainWnd,s);
return true;
}
static LONG_PTR CALLBACK MainWndProc(HWND Wnd,UINT msg,WPARAM wParam,LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
hMainWnd=Wnd;
hMainMenu=GetMenu(Wnd);
hStatus=CreateStatusWindow(WS_VISIBLE|WS_CHILD,TEXT("Status"),Wnd,13);
hEdit=CreateWindowEx(0,RICHEDIT_CLASS,0,WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_AUTOHSCROLL|ES_AUTOVSCROLL|ES_WANTRETURN,
0,0,0,0,Wnd,(HMENU)12,0,0);
msgFind=RegisterWindowMessage(FINDMSGSTRING);
findreplace.lStructSize=sizeof findreplace;
findreplace.hwndOwner=Wnd;
findreplace.Flags=FR_SHOWHELP|FR_DOWN;
findreplace.lpstrFindWhat=findstr;
findreplace.wFindWhatLen=elemof(findstr);
findreplace.lpstrReplaceWith=replacestr;
findreplace.wReplaceWithLen=elemof(replacestr);
}break;
case WM_SIZE: {
SendMessage(hStatus,msg,wParam,lParam);
RECT rc;
static const int info[]={1,1,1,13,0};
GetEffectiveClientRect(Wnd,&rc,const_cast<int*>(info));
SetWindowPos(hEdit,0,rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,SWP_NOZORDER);
}break;
case WM_INITMENU: {
EnableMenuItem((HMENU)wParam,65,SendMessage(hEdit,EM_CANUNDO,0,0)?MF_ENABLED:MF_GRAYED);
EnableMenuItem((HMENU)wParam,66,SendMessage(hEdit,EM_CANREDO,0,0)?MF_ENABLED:MF_GRAYED);
}break;
case WM_COMMAND: switch (wParam) {
case 65: SendMessage(hEdit,EM_UNDO,0,0); break;
case 66: SendMessage(hEdit,EM_REDO,0,0); break;
case 81: if (hFind) SetActiveWindow(hFind);
else hFind=FindText(&findreplace);
break;
case 84: if (hFind) SetActiveWindow(hFind);
else hFind=ReplaceText(&findreplace);
break;
case 101: openfile(Wnd); break;
case 102: apply(Wnd); break;
case 103: ShellExecute(Wnd,0,ExeName,0,0,SW_SHOWDEFAULT); break;
case 104: revert(Wnd); break;
case 112: handleDependency(wParam,0x6595b64144ccf1df,"Microsoft.Windows.Common-Controls","6.0.0.0"); break;
case 319: handleDependency(wParam,0x1fc8b3b9a1e18e3b,"Microsoft.VC80.CRT","8.0.50608.0"); break;
case 320: handleDependency(wParam,0x1fc8b3b9a1e18e3b,"Microsoft.VC90.DebugCRT","9.0.21022.8"); break;
case 301: handleOS(wParam,0xe2011457154643c5,0xa5fe008deee3d3f0); break; // Vista
case 302: handleOS(wParam,0x35138b9a5d964fbd,0x8e2da2440225f93a); break; // 7
case 303: handleOS(wParam,0x4a2f28e353b94441,0xba9cd69d4a4a6e38); break; // 8
case 304: handleOS(wParam,0x1f676c7680e14239,0x95bb83d0f6d0da78); break; // 8.1
case 305: handleOS(wParam,0x8e0f7a12bfb34fe8,0xb9a548fd50a15a9a); break; // 10
case 309:
case 310:
case 311: setDpiAware(wParam-309); break;
case 2: SendMessage(Wnd,WM_CLOSE,0,0); break;
case MAKELONG(12,EN_CHANGE):
case MAKELONG(12,EN_UPDATE): { // Nur EN_UPDATE kommt
EnableMenuItem(hMainMenu,102,Edit_GetModify((HWND)lParam)?MF_ENABLED:MF_GRAYED);
}break;
}break;
case WM_NOTIFY: switch (wParam) {
case 12: {
NMHDR*nmhdr=(NMHDR*)lParam;
switch (nmhdr->code) {
case EN_UPDATE: // kommt nie
case EN_CHANGE: EnableMenuItem(hMainMenu,102,Edit_GetModify(nmhdr->hwndFrom)?MF_ENABLED:MF_GRAYED); break;
}
}break;
}break;
case WM_SETFOCUS: SetFocus(hEdit); break;
case WM_CLOSE:
case WM_QUERYENDSESSION: {
if (!(GetMenuState(hMainMenu,102,0)&MF_GRAYED)) {
switch (MBox(Wnd,9,MB_YESNOCANCEL|MB_ICONQUESTION|MB_SOUND,ExeName)) {
case IDNO: goto def; // don't save, Windows can terminate
case IDCANCEL: return FALSE; // don't save, don't let Windows terminate
}
apply(Wnd); // save, then let Windows terminate
}
}break;
// case WM_CLOSE: DestroyWindow(Wnd); break;
case WM_DESTROY: PostQuitMessage(0); break;
default: if (msg==msgFind) {
FINDREPLACE&fr=*(FINDREPLACE*)lParam;
if (fr.Flags&FR_DIALOGTERM) hFind=0;
if (fr.Flags&FR_FINDNEXT) {
UINT options=fr.Flags&(FR_DOWN|FR_MATCHCASE|FR_WHOLEWORD);
FINDTEXTEX ft={{0,-1},fr.lpstrFindWhat};
if (SendMessage(hEdit,EM_FINDTEXTEX,options,(LPARAM)&ft)>=0) {
Edit_SetSel(hEdit,ft.chrgText.cpMin,ft.chrgText.cpMax);
}else MessageBeep(MB_ICONEXCLAMATION);
}
}
}
def:
return DefWindowProc(Wnd,msg,wParam,lParam);
}
void WinMainCRTStartup() {
hInstance=GetModuleHandle(0);
InitCommonControls();
LoadLibrary(TEXT("Riched20.dll"));
WNDCLASS wc={
CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
MainWndProc,
0,0,0,
LoadIcon(0,IDI_APPLICATION),
0,0,
MAKEINTRESOURCE(100),
MAKEINTATOM(100)
};
RegisterClass(&wc);
// const TCHAR*cmdline=GetCommandLine();
// const TCHAR*args=PathGetArgs(cmdline);
LoadString(hInstance,1,MBoxTitle,elemof(MBoxTitle));
HACCEL hAccel=LoadAccelerators(hInstance,MAKEINTRESOURCE(100));
hMainWnd=CreateWindowEx(0,MAKEINTATOM(100),MBoxTitle,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
0,0,0,0);
ShowWindow(hMainWnd,SW_SHOWDEFAULT);
MSG msg;
while (GetMessage(&msg,0,0,0)) {
if (hAccel && TranslateAccelerator(hMainWnd,hAccel,&msg)) continue;
if (hDialog && IsDialogMessage(hDialog,&msg)) continue;
if (hFind && IsDialogMessage(hFind,&msg)) continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
ExitProcess((UINT)msg.wParam);
}
// Normalerweise wäre _alloca(size_t) ein simples "sub esp,eax".
// Aber das Win32-Speicherlayout macht einen Strich durch die Rechnung und verlangt,
// dass der Stack Seite für Seite abwärts belegt wird.
// Sonst kommt eine Schutzverletzung.
// Oder man legt per Linker-Schalter ein reichliches Commit für den Stack fest. (Geht das??)
// Daher generiert der Compiler für _alloca() sowie für Funktionen mit mehr als 4 KByte
// lokale Variablen einen Aufruf dieser Funktion.
// Prinzipiell könnte diese eax/4 push-Befehle ausführen …
// PE: eax = Zu reservierende Bytes
// PA: esp = Anfang des Speicherbereiches
// VR: eax
extern "C" void _declspec(naked) _alloca_probe() {_asm{
add eax,3
and al,~3 // Teilbarkeit durch 4 sicherstellen
push ecx
lea ecx,[esp]+8 // ecx = Endadresse auf Stack
jmp short testpg
onepg: sub ecx,4096
sub eax,4096
test dword ptr [ecx],eax // seitenweise Speicherlesezyklus anstoßen
testpg: cmp eax,4096
jae short onepg
sub ecx,eax // ecx = Startadresse auf Stack
mov eax,esp // eax = Zeiger auf ursprüngliches ecx
test dword ptr [ecx],eax // nochmal Speicherlesezyklus
mov esp,ecx // esp = Startadresse (Rückgabewert)
mov ecx,[eax] // ecx restaurieren
jmp dword ptr[eax+4] // Rücksprung zum Aufrufer
}}
extern "C" void _declspec(naked) _aullshr() {_asm{
test cl,32
jnz l1
shrd eax,edx,cl
shr edx,cl
ret
l1: mov eax,edx
xor edx,edx
shr eax,cl
ret
}}
Detected encoding: ANSI (CP1252) | 4
|
|